考慮下面的代碼...OpenMP/C++:並行循環與事後還原 - 最佳實踐?
for (size_t i = 0; i < clusters.size(); ++i)
{
const std::set<int>& cluster = clusters[i];
// ... expensive calculations ...
for (int j : cluster)
velocity[j] += f(j);
}
...我想在多個CPU /內核上運行。功能f
不使用velocity
。
一個簡單的#pragma omp parallel for
在第一個for循環之前會產生不可預知/錯誤的結果,因爲在內部循環中修改了std::vector<T> velocity
。多個線程可以訪問並(嘗試)同時修改同一個元素velocity
。
我認爲第一個解決方案是在velocity[j] += f(j);
操作之前編寫#pragma omp atomic
。這給我一個編譯錯誤(可能與Eigen::Vector3d
類型的元素或velocity
是類成員有關)。此外,我讀原子操作非常緩慢相比,每個線程有一個私人變量,並在最後減少。所以這就是我想要做的,我想。
我想出了這一點:
#pragma omp parallel
{
// these variables are local to each thread
std::vector<Eigen::Vector3d> velocity_local(velocity.size());
std::fill(velocity_local.begin(), velocity_local.end(), Eigen::Vector3d(0,0,0));
#pragma omp for
for (size_t i = 0; i < clusters.size(); ++i)
{
const std::set<int>& cluster = clusters[i];
// ... expensive calculations ...
for (int j : cluster)
velocity_local[j] += f(j); // save results from the previous calculations
}
// now each thread can save its results to the global variable
#pragma omp critical
{
for (size_t i = 0; i < velocity_local.size(); ++i)
velocity[i] += velocity_local[i];
}
}
這是一個很好的解決方案嗎?是否最佳解決方案? (是否正確?)
進一步的想法:使用reduce
子句(而不是critical
部分)會引發編譯器錯誤。我認爲這是因爲velocity
是一個班級成員。
我試圖找到一個類似問題的問題,並且this問題看起來像是幾乎相同。但我認爲我的案例可能不同,因爲最後一步包括for
循環。此外,這是否是最好的方法仍然成立。
編輯:按註釋要求:reduction
條款等
#pragma omp parallel reduction(+:velocity)
for (omp_int i = 0; i < velocity_local.size(); ++i)
velocity[i] += velocity_local[i];
...引發以下錯誤:
錯誤C3028: 'ShapeMatching ::速度':僅可變數據或靜態數據成員可用於數據共享條款
(與g++
類似的錯誤)
分享使用減少錯誤,從而使修復可以建議的代碼。 – Jeff
@Jeff完成了。 [足夠的字符] – Micha
你有沒有考慮過PPL?編寫「自減少數據」的代碼在那裏很靈活,並且不一定是原語。基本上,你描述了線程加載數據是什麼,以及如何組合兩個線程本地數據,剩下的就完成了。 – Yakk