為什麼伺服器linux下用openmp結果比單線程還要慢?
一個並行演算法試圖用openMP加速.
在雙核4線程win8的visual studio2012下能夠拿到大約2.5x的speedup. 但是拿到linux伺服器上測試居然比單線程還要慢好多, 可能的原因是什麼呢? 伺服器的CPU應該是8核16線程拿openmp加速這個演算法純粹出於好奇, openmp剛接觸沒幾天程序結構大抵這樣:
#include&
float my_func(int Npath)
{
float v;
v=0;omp_set_num_threads(16)
#pragma omp parallel for reduction(+:v)
for(int i=0;i&用g++ -fopenmp -O3 -o my my_func.cpp編譯.
主要是兩個問題,任務調度和oversubscription。
openmp默認使用的schedule是取決於編譯器實現的。gcc默認使用schedule(dynamic,1),也就是動態調度並且塊大小是1。在你的程序裡面,這種調度是及其低效的,看代碼都能預期到,不太可能比單線程快。
動態調度的一種簡單理解方式是,計算任務存在一個任務隊列裡面,你的for循環每一個i值對應一個計算任務。每個線程每次提取一批任務,然後計算。「一批」是多少呢?就是前面說的塊大小,在你的程序裡面是1。提取任務需要什麼操作呢?因為這個任務隊列是多線程共享的,提取任務前必須加鎖,讀取一批,從隊列中移除,然後解鎖。說到這裡,你應該已經知道原因了。
你的線程一次只提取一次計算任務,這個任務還完成得很快。然後所有的16個線程排著隊,逐個去加鎖,搶任務,然後解鎖讓其它線程繼續搶。然後馬上發現這個任務很快,又要重新去排隊等任務,始終處於飢餓狀態。注意排隊的時候可能也是要佔cpu的,因為使用了busy wait,所以可能你看來十六核滿負荷,但是其實啥也沒幹。
我的建議就是,使用static schedule,或者增加dynamic schedule的塊大小,比如1024,取決於你循環多少次。一般如果你知道每次循環的執行時間基本都是一樣,並且是專用伺服器設置好affinity,無其它負荷無oversubscription無numa問題的話,static schedule會是個比較好的選擇。這樣每個線程做哪些任務只需要進行一次分配,最小化了openmp本身的消耗。
還有一個非常重要的問題!數值計算不要使用cpu超線程!cpu的超線程對於數值計算基本是有害無益的,線程數不要大於實際核數,否則就是oversubscription。你這已經是非常嚴重的oversubscription了。數值計算專用的話,建議直接關閉伺服器bios裡面的超線程選項。在一個平台上能看到加速,應該不是應用的問題。可能是一些implementation defined的環境變數默認值的影響。或者你的Linux被設置了taskset導致有些CPU不能使用。找一個支持OpenMP 4.0 OMP_DISPLAY_ENV的版本,設置OMP_DISPLAY_ENV=TRUE或者VERBOSE,看看環境變數設置有什麼不一樣。還有把thread數設成2個試試。
在伺服器上有可能是有很多人的任務同時在跑,或者你的優先順序不夠高,被分到的資源太少。
這裡有鎖,你每個線程都會訪問v。正確的做法是每個線程起一個v_local計算加和。然後循環結束再openmp atomic把他加總到v上。
#include&
float my_func(int Npath)
{
float v;
v=0;
omp_set_num_threads(16)
#pragma omp parallel
{
float v_local=0.0;
#pragma omp for
for(int i=0;i&
貌似 OpenMP Scheduling
遇到了一個類似的問題。一個算離散卷積的function,大約524288*524288的複雜度。
由別人的fortran程序改寫過來,因為要跟自己的C++程序整合到一起。兩者核心部分內容完全一樣,只在數據類型上有區別,例如用vector取代了數組。程序的循環部分是雙層嵌套循環,外部for(i=0;i&<524288;i++),內層同樣,omp指令加在內層循環上,用到了private和reduction命令。
在CentOS 6.7伺服器上進行編譯,fortran代碼用ifort 12.1,C++代碼用g++ 4.8編譯。編譯完成後,先測試了內層的單個for循環,fortran和C++都是55ms左右。之後開始加上外部循環進行測試,先循環了從0到511,fortran代碼用CPU_TIME函數給出的時間是26000ms左右,C++代碼用clock_gettime函數給出的時間類似,也是26000ms左右。然而問題出來了,fortran代碼的實際運行時間是2.6s左右,快了十倍,而C++代碼的運行時間則實打實的是26s。
用top命令查看CPU佔用,顯示兩者運行時都佔用10個CPU,然而顯然fortran代碼是真的調用了十個線程在算,提高了十倍;C++代碼的速度則和串列差不多。
有沒有人了解可能的原因?
代碼比較長,只把主要的循環體結構寫出來int nl=524288;vector&sum+=a.at(il2);
} cout&<&如果你是intell 處理器試試用apt-get 安裝 libiomp5
遇到同樣的問題了#include "stdlib.h"#include &
void test()
{ int a = 0; clock_t t1 = clock(); for (int i = 0; i &< 1000000000; i++) { a = i+1; } clock_t t2 = clock(); printf("Time = %d", t2-t1);
}
int main(int argc, char* argv[]){ for(int num_thread=1;num_thread&<32;num_thread=num_thread*2) { omp_set_num_threads(num_thread); clock_t t1 = clock(); #pragma omp parallel { test();}
clock_t t2 = clock(); printf("threads=%d, Total time = %d", num_thread, t2-t1); } test(); return 0;}16核32線程,主頻2.0G 伺服器 VS2010下測試結果
同樣的代碼在linux伺服器中:
16核32線程2.6GHz,centos6.6,gcc -fopenmp my.cpp./a.out結果:為什麼時間會線性增加?推薦閱讀:
※哪些因素限制了ANSYS Fluent的並行核數?
※關於並行計算(單CPU多核並行,單節點多CPU並行,多節點並行)的效率快慢問題?
※Xeon E5 2xxx 核心數/價格 的問題?
※MPP 與 Hadoop是什麼關係?
※如何讓我的matlab內存使用率達百分之百?