為什麼伺服器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& a(nl);

(賦值a)

for (int il1=0;il1&{

#pragma omp parallel for privite(...) reduction(+:sum)

for(int il2=0;il2& {

sum+=a.at(il2);

}

cout&<&}


如果你是intell 處理器試試用apt-get 安裝 libiomp5


遇到同樣的問題了

#include "stdlib.h"

#include &

#include &

#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內存使用率達百分之百?

TAG:Linux | 並行計算 | 多線程 | OpenMP |