2013-10-19 97 views
1

我最近開始研究OpenMP,因爲我將研究一些高度計算昂貴的圖像分析項目。我使用帶有Intel i7(8核)和mingw64 gcc 4.8.1的Windows 7。我在Code :: Blocks中編寫代碼,並且爲了編譯和運行它而設置了一切。在我的代碼的幾個部分,我會做一些像素操作,我認爲這將是一個很好的候選人並行處理。令我驚訝的是,事實證明,順序比並行處理更快。我爲32位和64位以及兩臺獨立的計算機嘗試了不同版本的gcc(4.7 - 4.8),但我總是遇到相同的性能問題。然後我試着用我以前在這兩臺電腦中的一臺上運行的舊版Visual Studio 2008運行它,爲此我獲得了預期的性能提升。因此,我的問題是 - 爲什麼我無法使用gcc看到相同的效果。有什麼我做錯了嗎?C++ OpenMP和gcc 4.8.1 - 並行化循環時的性能問題

這是一個最小工作示例。

#include <omp.h> 
#include <cstdlib> 
#include <iostream> 

int main(int argc, char * argv[]) 
{ 
    /* process a stack of images - set the number to 1000 for testing */ 
    int imgStack = 1000; 

    double start_t = omp_get_wtime(); 
    for (int img = 0; img < imgStack; img++) 
    { 
     omp_set_num_threads(8); 
     #pragma omp parallel for default(none) 
     for (int y = 0; y < 1000000000; y++) /* increased the number of pixels to make it worthwhile and to see a difference*/ 
     { 
     for (int x = 0; x < 1000000000; x++) 
     { 
      unsigned char pixel[4]; 
      pixel[0] = 1; 
      pixel[1] = 2; 
      pixel[2] = 3; 
      pixel[3] = 4; 

      /* here I would do much more but removed it for testing purposes */ 

     } 
     } 
    } 
    double end_t = (omp_get_wtime() - start_t) * 1000.0; 
    std::cout << end_t << "ms" << std::endl; 

    return 0; 
} 

在建築日誌我有以下

x86_64-w64-mingw32-g++.exe -Wall -O2 -fopenmp -c C:\Code\omptest\main.cpp -o obj\Release\main.o 
x86_64-w64-mingw32-g++.exe -o bin\Release\omptest.exe obj\Release\main.o -s C:\mingw-builds\x64-4.8.1-posix-seh-rev5\mingw64\bin\libgomp-1.dll 

輸出以下

for 1 thread : 43ms 
for 8 threads: 594ms 

我也試圖關閉優化(-O0)的情況下,編譯做一些循環展開。我閱讀了關於虛假共享的問題,因此我將循環內的任何變量保留爲私有的,以確保這不是問題。我不擅長分析,因此我無法分辨下面會發生什麼,例如引起所有線程等待的內部鎖。

我不明白我在這裏做錯了什麼。

- 編輯 -

感謝大家。在我的真實代碼中,我有一個包含2000個圖像的圖像堆棧,每個圖像大小爲2000x2000像素。我試圖簡化這個例子,這樣每個人都可以很容易地重現這個問題,在這個問題中,我簡化了它,導致了其他問題的後果。你們都完全正確。 在我的真實代碼中,我使用Qt打開並顯示了我的圖像,以及我自己的圖像管理器,該圖像管理器加載並遍歷堆棧,一次給出一張圖像。我認爲提供整個樣本只會太多而且使事情複雜化(即不提供最低工作示例)。

我將所有變量(imageHeight,imageWidth等)作爲常量傳遞給我的圖像作爲共享。最初那是一個指向QImage的指針。在循環中,我使用qtimg-> setPixel(...)設置了最終的像素值,似乎MSVC編譯器處理與gcc編譯器相比的不同。最後,我用一個指向unsigned char數組的指針替換了QImage指針,這給了我預期的性能提升。

@Hristo Iliev:感謝有關線程池的信息。這真的很好知道。

+0

你在內循環中根本沒有做任何事情。編譯器應該完全優化它,所以你會看到的是設置線程和向他們分發(不)工作的成本。 – pburka

+0

如果'QImage :: setPixel()'使用內部鎖,例如爲了使操作線程安全,一次從多個線程調用它只會將其執行序列化。 –

回答

1

到由於pixels只被分配到再沒有用過,整個內環得到完全由GCC的優化器去除與-O2作爲一個可以很容易地通過啓用樹來驗證轉儲:

; Function <built-in> (main._omp_fn.0, funcdef_no=1036, decl_uid=21657, cgraph_uid=256) 

<built-in> (void * .omp_data_i) 
{ 
<bb 2>: 
    return; 

} 

和你這樣做的目的是有效衡量OpenMP運行時開銷。

使用-O0所有的代碼都保留在原位,並且運行時間按照預期的線程數進行縮放,但我懷疑你是否曾用1000000000 x 1000000000的圖像對它進行過測試。

1

鑑於代碼示例,我無法重複您的結果。你必須顯示你的真實堆棧大小和圖像大小。因爲如果使用1個線程只能在5ms內完成工作,多線程不會使其更快。啓動多個線程會帶來很大的開銷,特別是當你啓動它們時。

+0

多年以來,GCC,MSVC,Intel和其他大多數OpenMP運行庫都實現了帶有線程池的工作線程。只有第一個平行區域是昂貴的。除非以後需要更多的線程,否則進一步輸入並行區域並不像您預期​​的那樣昂貴。 –