[英]C++ OpenMP directives
我有一個循環,我正在嘗試並行化,並在其中我填充容器,說一個STL地圖。 然后考慮下面的簡單偽代碼,其中T1和T2是一些任意類型,而f和g是整數參數的一些函數,分別返回T1,T2類型:
#pragma omp parallel for schedule(static) private(i) shared(c)
for(i = 0; i < N; ++i) {
c.insert(std::make_pair<T1,T2>(f(i),g(i))
}
這看起來相當直觀,似乎應該是平凡的並行化,但它並沒有像我預期的那樣加速。 相反,它導致我的代碼中的運行時錯誤,因為容器中填充了意外值,可能是由於競爭條件。 我甚至嘗試過設置障礙而不是什么,但一切都沒有用。 允許它工作的唯一方法是使用如下的關鍵指令:
#pragma omp parallel for schedule(static) private(i) shared(c)
for(i = 0; i < N; ++i) {
#pragma omp critical
{
c.insert(std::make_pair<T1,T2>(f(i),g(i))
}
}
但是這種渲染在上面的例子中使用omp的全部意義毫無用處,因為一次只有一個線程正在執行循環的大部分(容器插入語句)。 我在這里錯過了什么? 如果不改變編寫代碼的方式,有人可以解釋一下嗎?
除非f()
和g()
是極其昂貴的函數調用,否則這個特殊的例子並不適合並行化。
STL容器不是線程安全的。 這就是你獲得比賽條件的原因。 因此,訪問它們需要同步 - 這使您的插入過程本身是順序的。
正如另一個答案所提到的那樣,並行性有很多開銷。 因此,除非f()
和g()
非常昂貴,否則你的循環沒有做足夠的工作來抵消並行性的開銷。
現在假設f()
和g()
是非常昂貴的調用,那么你的循環可以像這樣並行化:
#pragma omp parallel for schedule(static) private(i) shared(c)
for(i = 0; i < N; ++i) {
std::pair<T1,T2> p = std::make_pair<T1,T2>(f(i),g(i));
#pragma omp critical
{
c.insert(p);
}
}
運行多線程代碼可以讓您考慮線程安全性和對變量的共享訪問。 只要你從多個線程開始插入c
,集合應該准備好進行這種“同時”調用並保持其數據一致,你確定它是這樣做的嗎?
另一件事是並行化有自己的開銷,當你嘗試在多個線程上運行一個非常小的任務時你不會獲得任何東西 - 分裂和同步的成本可能會導致任務的總執行時間更長。
正如你猜測的那樣, c
會有明顯的數據競爭。 STL映射不是線程安全的。 在多個線程中同時調用insert
方法將產生非常不可預測的行為,大多數情況下只是崩潰。
是的,為避免數據爭用,您必須擁有(1)像#pragma omp critical
這樣的互斥鎖,或者(2) 並發數據結構 (也稱為無外觀數據結構)。 但是,並非所有數據結構都可以在當前硬件中無鎖定。 例如,TBB提供tbb::concurrent_hash_map
。 如果您不需要訂購密鑰,您可以使用它並且可以獲得一些加速,因為它沒有傳統的互斥鎖。
如果您只使用哈希表並且表格非常龐大,那么您可以采用類似於減少的方法(請參閱此鏈接以了解減少的概念)。 哈希表不關心插入的順序。 在這種情況下,您為每個線程分配多個哈希表,並讓每個線程並行插入N /#個線程項,這將提供加速。 通過並行訪問這些表也可以輕松完成查找。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.