簡體   English   中英

為什么並行化會如此顯着地降低性能?

[英]Why would parallelization decrease performance so dramatically?

我有一個OpenMP程序(數千行,不可能在這里重現),其工作原理如下:

它由工作線程和任務隊列組成。
任務包括卷積; 每當工作線程從工作隊列中彈出一個任務時,它就會執行所需的卷積,並可選擇將更多的卷積推送到隊列中。
(沒有特定的“主”線程;所有工人都是平等的。)

當我在自己的機器上運行這個程序( 4核HT非NUMA Core i7 )時,我得到的運行時間是:

(#threads: running time)
 1: 5374 ms
 2: 2830 ms
 3: 2147 ms
 4: 1723 ms
 5: 1379 ms
 6: 1281 ms
 7: 1217 ms
 8: 1179 ms

這是有道理的。

但是,當我在NUMA 48核AMD Opteron 6168機器上運行時,我得到了這些運行時間:

 1: 9252 ms
 2: 5101 ms
 3: 3651 ms
 4: 2821 ms
 5: 2364 ms
 6: 2062 ms
 7: 1954 ms
 8: 1725 ms
 9: 1564 ms
10: 1513 ms
11: 1508 ms
12: 1796 ms  <------ why did it get worse?
13: 1718 ms
14: 1765 ms
15: 2799 ms  <------ why did it get *so much* worse?
16: 2189 ms
17: 3661 ms
18: 3967 ms
19: 4415 ms
20: 3089 ms
21: 5102 ms
22: 3761 ms
23: 5795 ms
24: 4202 ms

這些結果非常一致,它不是機器上的負載神器。
所以我不明白:
什么可能導致性能在12核之后下降如此之多?

我會明白,如果在一定程度上飽和性能(我能責怪有限的內存帶寬),但我不明白它如何能夠從1508毫秒通過增加更多的線程下降到5795毫秒。

這怎么可能?

這種情況很難弄明白。 一個關鍵是查看內存位置。 如果沒有看到你的代碼,就不可能完全說出出了什么問題,但我們可以討論一些讓“多線程不那么好”的事情:

在所有NUMA系統中,當內存位於處理器X且代碼在處理器Y上運行時(其中X和Y不是同一處理器),每次內存訪問都會對性能造成不利影響。 因此,在正確的NUMA節點上分配內存肯定會有所幫助。 (這可能需要一些特殊的代碼,例如設置親和力掩碼,並至少提示您希望Numa感知分配的OS /運行時系統)。 至少,確保您不是簡單地處理由“第一個線程分配,然后啟動更多線程”的一個大型數組。

另一件更糟糕的事情是共享或錯誤共享內存 - 所以如果兩個或多個處理器使用相同的緩存行,那么你將在這兩個處理器之間獲得乒乓匹配,其中每個處理器將執行“我想要內存”在地址A“,獲取內存內容,更新它,然后下一個處理器將執行相同的操作。

結果在12個線程中變壞的事實似乎表明它與“套接字”有關 - 要么是共享數據,要么數據位於“錯誤的節點上”。 在12個線程中,您可能開始使用第二個套接字(更多),這將使這些問題更加明顯。

為獲得最佳性能,您需要在本地節點上分配內存,不需要共享,也不需要鎖定。 你的第一組結果看起來也不是“理想的”。 我有一些(絕對非共享)代碼,它給處理器數量提供了n倍的好處,直到我用完處理器(不幸的是,我的機器只有4個內核,所以它不是很好,但它仍然好4倍超過1核心,如果我得到了48或64核機器,那么在計算“怪異數字”時會產生48或64個更好的結果。

編輯:

“套接字問題”是兩件事:

  1. 內存位置:基本上,內存連接到每個套接字,因此如果內存是從屬於“上一個”套接字的區域分配的,那么讀取內存會有額外的延遲。

  2. 緩存/共享:在處理器內,存在共享數據的“快速”鏈接(通常是“底層共享緩存”,例如L3緩存),這允許套接字內的核心比與其中的核心更有效地共享數據。一個不同的插座。

所有這些都類似於維修汽車,但你沒有自己的工具箱,所以每次你需要一個工具時,你都要問你旁邊的同事用螺絲刀,15毫米扳手,或者你需要的任何東西。 然后在工作區域滿員時將工具返回。 這不是一種非常有效的工作方式......如果你擁有自己的工具(至少是最常見的工具 - 你每月只使用一次的特殊扳手之一不是一個大問題,那就更好了,但你常見的10,12和15毫米扳手和一些螺絲刀,肯定)。 當然,如果有四種機制,共享相同的工具箱,情況會更糟。 這是在四插槽系統中“在一個節點上分配所有內存”的情況。

現在想象你有一個“扳手盒”,只有一個機械師可以使用扳手盒,所以如果你需要一個12毫米扳手,你必須等待你旁邊的人完成使用15毫米扳手。 如果您有“虛假緩存共享” - 處理器實際上沒有使用相同的值,但由於緩存行中有多個“東西”,處理器正在共享緩存行(緩沖區框),會發生這種情況。

我有兩個建議:

1.)在NUMA系統上,您要確保寫入的緩沖區與頁面邊界對齊,也是頁面的倍數。 頁面通常為4096字節。 如果在頁面之間拆分緩沖區,則會出現錯誤共享。

http://dl.acm.org/citation.cfm?id=1295483

當共享存儲器並行系統中的處理器在相同的相干塊(高速緩存行或頁面)內引用不同的數據對象時,發生錯誤共享,從而引起“不必要的”一致性操作。

和這個鏈接https://parasol.tamu.edu/~rwerger/Courses/689/spring2002/day-3-ParMemAlloc/papers/lee96effective.pdf

...當可能具有不同訪問模式的幾個獨立對象被分配給相同的可移動存儲器單元時發生的錯誤共享(在我們的例子中,是一個虛擬存儲器頁面)。

因此,例如,如果一個數組是5000字節,你應該使它成為8192字節(2 * 4096)。 然后用類似的東西將它對齊

float* array = (float*)_mm_malloc(8192, 4096);  //two pages both aligned to a page

在非NUMA系統上,您不希望多個線程寫入同一緩存行(通常為64個字節)。 這會導致錯誤共享。 在NUMA系統上,您不希望多個線程寫入同一頁面(通常為4096字節)。

請參閱此處的一些注釋在不使用臨界區的情況下,與OpenMP並行填充直方圖(數組縮減)

2.)OpenMP可以將線程遷移到不同的核心/處理器,因此您可能希望將線程綁定到某些核心/處理器。 您可以使用ICC和GCC執行此操作。 使用GCC我認為你想做一些像GOMP_CPU_AFFINITY=0 2 4...看到這個鏈接在這個簡單的OpenMP程序中有什么限制擴展?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM