在 C 應用程式中嘗試使用 OpenMP 時,我遇到了嚴重的性能問題,與單執行緒相比,多執行緒性能可能會差 1000 倍。僅當至少一個內核被另一個行程最大化時才會發生這種情況。
經過一番挖掘,我可以將問題隔離為一個小例子,我希望有人能對這個問題有所了解!
最小的例子
這是一個說明問題的最小示例:
#include <iostream>
int main() {
int sum = 0;
for (size_t i = 0; i < 1000; i ) {
#pragma omp parallel for reduction( :sum)
for (size_t j = 0; j < 100; j ) {
sum = i;
}
}
std::cout << "Sum was: " << sum << std::endl;
}
我需要將 OpenMP 指令置于外部 for 回圈內,因為我的真實代碼在相互依賴的時間步長上回圈。
我的設定
我在帶有 AMD Ryzen 9 5900X(12 核,24 執行緒)的 Ubuntu 21.04 上運行該示例,并使用 G 10.3.0 編譯它g -fopenmp example.cc。
基準測驗
如果您在后臺沒有任何其他內容的情況下運行此程式,它會迅速終止:
> time ./a.out
Sum was: 999000
real 0m0,006s
user 0m0,098s
sys 0m0,000s
但是如果一個內核被另一個行程使用,它運行得非常慢。在這種情況下,我跑去stress -c 1完全使用后臺的內核來模擬另一個行程。
> time ./a.out
Sum was: 999000
real 0m8,060s
user 3m2,535s
sys 0m0,076s
這是 1300 倍的放緩。我的機器有 24 個并行執行緒,因此當一個執行緒繁忙而其他 23 個執行緒可用時,理論上的減速應該只有 4% 左右。
發現
問題似乎與 OpenMP 如何分配/分配執行緒有關。
- 如果我將 omp 指令移到外回圈,問題就會消失
- 如果我明確地將執行緒數設定為 23,問題就會消失 (
num_threads(23)) - 如果我明確地將執行緒數設定為 24,問題仍然存在
- 行程終止所需的時間從 1-8 秒不等
- 該程式在運行時不斷使用盡可能多的 cpu,我假設大多數 OpenMP 執行緒都在自旋鎖中
From these findings it would seem like OpenMP assigns the jobs to all cores, including the one that is already maxed out, and then somehow forcing each individual core to finish its tasks and not allowing them to be redistributed when other cores are done.
I have tried changing the scheduling to dynamic but that didn't help either.
I would be very helpful for any suggestions, I'm new to OpenMP so it's possible that I've made a mistake. What do you make of this?
uj5u.com熱心網友回復:
所以這是我能弄清楚的:
運行程式OMP_DISPLAY_ENV=verbose(有關環境變數串列,請參見https://www.openmp.org/spec-html/5.0/openmpch6.html)
詳細設定將顯示您OMP_WAIT_POLICY = 'PASSIVE'和GOMP_SPINCOUNT = '300000'. 換句話說,當一個執行緒必須等待時,它會在進入睡眠之前旋轉一段時間,消耗CPU時間并阻塞一個CPU。每次執行緒到達回圈末尾或主執行緒分發 for 回圈之前,或者甚至在并行部分開始之前,都會發生這種情況。
因為 GCC 的 libgomp 不使用pthread_yield,這有效地阻塞了一個 CPU 執行緒。因為運行的軟體執行緒比 CPU 執行緒多,一個執行緒將不會運行,導致所有其他執行緒忙于等待,直到內核調度程式重新分配 CPU。
如果您使用 呼叫您的程式OMP_WAIT_POLICY=passive,GCC 將設定GOMP_SPINCOUNT = '0'. 然后內核會立即讓等待的執行緒進入睡眠狀態并允許其他執行緒運行。現在你的表現會好很多。
有趣的是OMP_PROC_BIND=true也有幫助。我認為不可移動的執行緒會以某種方式影響內核調度程式,這對我們有好處,但我不確定。
Clang 的 OpenMP 實作不會受到這種性能下降的影響,因為它使用pthread_yield. 當然,如果系統呼叫開銷很大并且在大多數計算環境中,這有其自身的缺點,因為您不應該過度使用 CPU。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/371850.html
