1

私は、互いの距離にx基づいてブロブをクラスター化する非常に単純なアルゴリズムを持っています。スレッドローカルデータでy使用するために同じものを移植しましたが、結果は正しくありませんでした。Parallel.Forつまり、同期を適切に使用して各スレッドを分離していなかった可能性があります。

2 つの実装の結果が異なる理由がまったくわかりません。任意の考えをいただければ幸いです。

完全にコンパイル可能なコードを投稿したかったのですが、使用されているオブジェクトがプロジェクトのコンテキストに緊密に統合されすぎています。アルゴリズムは非常に単純なので、邪魔にならないことを願っています。

クラス レベルの宣言:

/// <summary>
/// Contains the master blobl collection to be clustered.
/// </summary>
public List<Blob> Blobs { get; private set; }

/// <summary>
/// List of clusters to be computed.
/// </summary>
public List<Cluster> Clusters { get; private set; }

線形の例 (正常に動作) :

Cluster cluster = null;

for (int i = 0; i < this.Blobs.Count; i++)
{
    cluster = new Cluster();

    cluster.Id = i;

    if (this.Blobs [i].ClusterId == 0)
    {
        cluster.Blobs.Add(this.Blobs [i], i);

        for (int j = 0; j < this.Blobs.Count; j++)
        {
            if (this.Blobs [j].ClusterId == 0)
            {
                if (this.Blobs [i].Rectangle.IntersectsWith(this.Blobs [j].Rectangle))
                {
                    cluster.Blobs.Add(this.Blobs [j], i);
                }
                else if (this.Blobs [i].Rectangle.IsCloseTo(this.Blobs [j].Rectangle, distanceThreshold))
                {
                    cluster.Blobs.Add(this.Blobs [j], i);
                }
            }
        }
    }

    if (cluster.Blobs.Count > 2)
    {
        this.Clusters.Add(cluster);
    }
}

パラレル ポート (不適切なクラスター) :

System.Threading.Tasks.Parallel.For<Cluster>
(
    0,
    this.Blobs.Count,
    new ParallelOptions() { MaxDegreeOfParallelism = degreeOfParallelism },
    () => new Cluster(),
    (i, loop, cluster) =>
    {
        cluster.Id = i;

        if (this.Blobs [i].ClusterId == 0)
        {
            cluster.Blobs.Add(this.Blobs [i], i);

            for (int j = 0; j < this.Blobs.Count; j++)
            {
                if (this.Blobs [j].ClusterId == 0)
                {
                    if (this.Blobs [i].Rectangle.IntersectsWith(this.Blobs [j].Rectangle))
                    {
                        cluster.Blobs.Add(this.Blobs [j], i);
                    }
                    else if (this.Blobs [i].Rectangle.IsCloseTo(this.Blobs [j].Rectangle, distanceThreshold))
                    {
                        cluster.Blobs.Add(this.Blobs [j], i);
                    }
                }
            }
        }

        return (cluster);
    },
    (cluster) =>
    {
        lock (this.Clusters)
        {
            if (cluster.Blobs.Count > 2)
            {
                this.Clusters.Add(cluster);
            }
        }
    }
);
4

1 に答える 1

1

あなたの問題は、その「スレッドローカルデータ」の誤解だと思います。のドキュメントにParallel.For()よると、次のとおりです。

[…] 同じスレッドで実行される反復間で共有されるローカル状態。

これが意味することは、ループの一部の繰り返しが同じClusterオブジェクトを共有することであり、これにより誤った結果が生じることになります。繰り返しごとにlocalInitと を実行すると、それらのコードをループの最初と最後に移動することでまったく同じことができるため、それらは役に立たなくなります。localFinally

デリゲートが存在する理由は、それらを最適化に使用できるからです。それらを使用すると、共有状態 (あなたの場合this.Clusters) に頻繁にアクセスする必要がなくなり、パフォーマンスが向上します。

この最適化が必要ない場合は、2 つのデリゲートを使用せず、代わりにループの本体を次のように記述します。

i =>
{
    var cluster = new Cluster { Id = i };

    // rest of the loop here

    if (cluster.Blobs.Count > 2)
    {
        lock (this.Clusters)
        {
            this.Clusters.Add(cluster);
        }
    }
}

(上記のコードでは、最適化としても に切り替えましたlock。 )if

スレッド ローカル データを使用した最適化が役立つ (つまり、実際に速度が向上する) と思われる場合は、それを使用できます。Clusterしかし、問題のデータは、単一の だけでなく、 のリストでなければなりませんCluster。何かのようなもの:

() => new List<Cluster>(),
(i, loop, clusters) =>
{
    var cluster = new Cluster { Id = i };

    // rest of the loop here

    if (cluster.Blobs.Count > 2)
        clusters.Add(cluster);

    return clusters;
},
clusters =>
{
    lock (this.Clusters)
    {
        this.Clusters.AddRange(clusters);
    }
}
于 2013-02-11T10:38:42.687 に答える