3

以下のコードのような関数があります。その目的は、3つのポイントがそれぞれ1つのファセットであるポイントの配列から一度に1つずつ三角形のファセットを取得し、それらをテッセレーションして、ファセットを、辺の長さがnodeSizeを超えない小さなファセットのリストに置き換えることです。

当然、この関数は現実的なファセットメッシュでは時間がかかります。粗い並列化を使用するようにリファクタリングしたいと思います。ただし、Parallel.Forには、インデックス番号を保持しながら、配列内のインデックスを一定間隔でステップスルーする方法がないようです。

SplitTriangleループ内の関数は計算上並列化に役立たないことを念頭に置いて、この関数をどのようにリファクタリングできますか?

Protected Shared Function SplitTriangles(Points As IEnumerable(Of Point3D), nodeSize As Single) As List(Of Point3D)
    Dim resultList As New List(Of Point3D)

        For i As Integer = 0 To Points.Count - 1 Step 3
            resultList.AddRange(SplitTriangle(Points(i), Points(i + 1), Points(i + 2), nodeSize * 4))
        Next

    Return resultList
End Function
4

3 に答える 3

2

ここでの最も簡単な解決策は、最初にポイントを調べて、それらを3ポイントグループの配列に分割することだと思います。Parallel.For次に、そのアレイで使用できます。

編集:あなたは何百万ものポイントを持っていて、これをいつもしているので、あなたは何か他のことをするべきです。

まず、Pointsコンテナが簡単にランダムアクセスできることを確認します(配列またはを使用しますList)。次に、これを行います。

  • resultList適切なサイズでを割り当てます。
  • いくつかの部分に分けPointsます(「いくつか」は見積もるのが難しい場合があります。これを少し試してみてください)。リストに12,000,000ポイントがあるとすると、 resultList4,000,000要素の長さになります。そして、4つの部分が最適な分割であると判断したとします。
    • 各パーツは連続している必要があります(0-3M、3M-6M、6M-9M、9M-12M)。
    • 最適な分割を見つけるのは簡単ではありませんが、些細な分割で十分であることがわかる場合があるので、今のところ心配する必要はありません。
  • 4つのスレッドがあり、それぞれが1つの部分を処理します(タスクAPIを使用できます。私の意見ではParallel.For、この場合よりもコードが明確になります。

スレッドセーフに関する注意:

List<Point>固定サイズの配列として使用する場合、スレッドセーフであると100%確信しているわけではありません。あるべきですが、100%になりたい場合は、必ず配列を使用してください。

于 2012-11-27T21:26:51.810 に答える
1

慣例により三角形として扱うIEnumerableofPointsを渡す代わりに、三角形を直接渡すと、コードははるかに単純になります。

ポイントを三角形に変換する場合、PLINQを使用して次のように記述できます。

    Function SplitTriangles(triangles As IEnumerable(Of Triangle3D), nodeSize As Single) As List(Of Triangle3D)
    Dim resultList As New List(Of Point3D)

    Dim results = (From triangle In triangles.AsParallel()
                   From newTriangle In SplitTriangle(triangle.A, triangle.B, triangle.C)
                   Select newTriangle).ToList()

    Return results
End Function

AsParallelは順序を保持しませんが、AsParallelの後に.AsOrderedを追加することで、これを強制できます。

Parallel.Forを使用するには、各計算の結果を収集するために、ローカルのイニシャライザーとファイナライザーを受け入れるオーバーロードを使用する必要があります。この場合、最終的なコードははるかに複雑であり、メリットはありません。

IEnumerable(Of Point3)の問題は、各トリプレットのポイント間に非常に強い関係があることです。Parallel.For / Foreachは、各アイテムが他のアイテムから独立しているリストに適しています。

ここでMarcGravellのPartitionメソッドを採用して、ポイントの初期リストを三角形のリストに変換し、ポイントをトリプレットにグループ化して、IEnumerableの代わりに三角形を返すことができます。

より単純ですが一般的ではない解決策は、ポイントから三角形を返すイテレータを作成することです。

 Iterator Function Triangulize(points As IEnumerable(Of Point3D)) As IEnumerable(Of Triangle3D)

    Dim count As Integer = 0

    Dim buffer(3) As Point3D

    For Each point As Point3D In points
        buffer(count) = point
        count += 1
        If count = 3 Then
            Yield New Triangle3D(buffer(0), buffer(1), buffer(2))
            count = 0
        End If
    Next

End Function
于 2012-11-28T12:03:04.537 に答える
0

このEnumerable.Range()関数を使用して、インデックスを生成できます。私はVB.NETにあまり詳しくないので、これをC#で記述します。

Enumerable.Range(0, Points.Count / 3).AsParallel().ForAll( loopVar => {
    var i = loopVar * 3;
    resultList.AddRange(SplitTriangle(Points(i), Points(i + 1), Points(i + 2), nodeSize * 4))
});

更新 このバージョンはスレッドセーフだと思いますが、スレッドセーフであり、結果が正しい順序になっていることを確認する必要があります。

resultList = Enumerable.Range(0, Points.Count / 3).AsParallel().SelectMany( loopVar => {
    var i = loopVar * 3;
    return SplitTriangle(Points(i), Points(i + 1), Points(i + 2), nodeSize * 4);
});
于 2012-11-27T21:31:40.163 に答える