ランダム アクセス コンテナ
最も簡単な解決策は、すべてをランダム アクセス コンテナー ( などstd::vector
) に入れ、OpenMP で好まれるインデックス ベースのループを使用することです。
// Copy elements
std::vector<size_t> neListVector(mesh->NEList[vid].begin(), mesh->NEList[vid].end());
// Process in a standard OpenMP index-based for loop
#pragma omp parallel for reduction(min : worst_q)
for (int i = 0; i < neListVector.size(); i++) {
worst_q = std::min(worst_q, complexCalc(neListVector[i]));
}
あなたの状況(size_t
簡単にコピーできるタイプの小さな要素)では、信じられないほど単純であることに加えて、これは最高のパフォーマンスとスケーラビリティを備えたソリューションでもあります。
コピーの回避
ただし、あなたの状況とは異なる状況では、要素が簡単にコピーされない (より大きな要素) か、まったくコピーできない場合があります。この場合、対応するポインターをランダム アクセス コンテナーにスローするだけです。
// Collect pointers
std::vector<const nonCopiableObjectType *> neListVector;
for (const auto &entry : mesh->NEList[vid]) {
neListVector.push_back(&entry);
}
// Process in a standard OpenMP index-based for loop
#pragma omp parallel for reduction(min : worst_q)
for (int i = 0; i < neListVector.size(); i++) {
worst_q = std::min(worst_q, mesh->element_quality(*neListVector[i]));
}
これは最初のソリューションよりも少し複雑ですが、小さな要素では同じように良好なパフォーマンスが得られ、大きな要素ではパフォーマンスが向上します。
タスクと動的スケジューリング
他の誰かが彼の回答で OpenMP タスクを取り上げたので、それについてコメントしたいと思います。タスクは非常に強力な構成要素ですが、オーバーヘッドが非常に大きく (スレッドの数が増えるとさらに増加します)、この場合は物事がより複雑になります。
メインスレッドでのタスクの作成は、それ自体を行うよりもはるかにコストがかかるため、min
削減のためにタスクの使用は決して正当化されません!std::min
より複雑な操作では、 の実行時間が反復間で大きく異なり、それを均等にするのに十分な反復がないmesh->element_quality
場合に、タスクの動的な性質が負荷分散の問題に役立つと考えるかもしれません。mesh->element_quality
しかし、その場合でも、より簡単な解決策があります。以前の解決策の 1 つで、行にschedule(dynamic)
ディレクティブを追加して動的スケジューリングを使用するだけです。parallel for
オーバーヘッドがはるかに少ない同じ動作を実現します。