2011-05-09 36 views
1

我將首先給出一些關於我遇到的問題的背景知識,以便您瞭解我正在嘗試做什麼。我一直在幫助開發一個特定的軟件工具,並發現使用OpenMP來並行化該軟件中的一些最大循環可以大大受益。實際上,我們實際上並行化了這些循環,只用兩個內核,循環執行速度提高了30%,這是一個不錯的改進。另一方面,我們注意到函數中的一個奇怪的現象,它使用遞歸調用遍歷樹結構。在OpenMP開啓的情況下,程序實際上減慢了,此功能的執行時間翻了一番。我們認爲樹形結構可能不夠平衡,並且在這個函數中註釋掉OpenMP pragmas。這似乎對執行時間沒有影響。目前我們正在使用帶有-fopenmp標誌的GCC-compiler 4.4.6來支持OpenMP。這裏是目前存在的問題:OpenMP會減慢程序而不是加快速度:gcc中的錯誤?

如果我們沒有在代碼中使用任何OMP編譯指示,一切運行良好。但是,如果我們只是下面的程序的主要功能,在從35秒雙打樹travelsal函數的執行時間開始增加爲75秒:

//beginning of main function 
... 
#pragma omp parallel 
{ 
#pragma omp single 
{} 
} 
//main function continues 
... 

有誰有關於爲什麼發生這種情況的任何線索?我不明白爲什麼程序使用OpenMP編譯指示很慢。如果我們取消所有的omp編譯指示,樹遍歷函數的執行時間會再次下降到35秒。我猜想這是某種編譯器錯誤,因爲我現在沒有其他解釋。

回答

1

並非所有可以並行化的東西都應該並行化。如果您使用單個線程,那麼只有一個線程執行它,而其他線程必須等到該區域完成。他們可以旋轉等待或睡覺。大多數實現都是以spin-wait開始的,希望單個區域不會花太長時間,等待的線程可以比睡眠時更快地看到完成。自旋等待會消耗很多處理器週期。您可以嘗試指定等待應該是被動的 - 但這僅在OpenMP V3.0中提供,並且只是實現的一個提示(因此它可能沒有任何作用)。基本上,除非在並行區域有很多工作可以補償單個區塊,否則單個區塊會大幅增加並行開銷,並且可能會使其並行化成本太高。

+1

感謝您的回答,但這仍不能解釋執行時間的增加。問題給出的代碼幾乎立即完成,但是會減慢程序的其餘部分,即使空的「編譯指示omp single」塊不應該以任何方式影響它。我開始懷疑,如果使用OpenMP實際上禁用某些編譯器優化,並因此在程序中的任何地方使用OpenMP **,都會在程序中的任何地方減慢代碼**的執行速度。 – Kuzin 2011-05-09 16:54:47

+1

根據編譯器的不同,可能會在OpenMP區域內禁用某些優化。但是,它不應該影響區域外的代碼。也就是說,我們已經看到了像內存分配器這樣的事情在OpenMP區域之後有更高的開銷,並且降低了速度。就你而言,如果代碼如你所示,那麼由於OpenMP的設置,你會看到一些額外的開銷。它不應該像你已經指出的那樣增加運行時間。你應該看看生成的代碼(-S),看看它與OpenMP不同。 – ejd 2011-05-09 18:50:34

1

首先,OpenMP通常會在第一次嘗試時降低性能。如果你不理解它,那麼使用omp parallel會非常困難。如果你能告訴我更多關於程序結構的信息,特別是由????註釋的以下問題,我可能會有所幫助。

//beginning of main function 
... 
#pragma omp parallel 
{ 

???? What goes here, is this a loop? if so, for loop, while loop? 

#pragma omp single 
    { 

    ???? What goes here, how long does it run? 
    } 
} 

//main function continues 
.... 
???? Does performance of this code reduce or somewhere else? 

謝謝。

+0

代碼就像它寫的一樣,在並行區域中除了一個空單區域外沒有任何內容。此代碼幾乎立即完成,僅用於測試目的。我目前沒有與我的代碼,但稍後在main中調用另一個函數,我們只需將其稱爲complexFunction。如果我們取出兩個omp編譯指示或者只是'單一'編譯指示,這個複合函數執行35秒。我的猜測是編譯器完全沒有空的平行區域。如果我們回寫兩個編譯指示,執行時間會再次增加到75秒。 – Kuzin 2011-05-10 05:04:09

1

我做了一些更多的測試,並做了一個小測試程序來測試問題是否可能與內存操作有關。我無法複製空白的並行單個區域的問題,導致程序在我的小測試程序中變慢,但是我能夠通過並行化一些malloc調用來複制減速。

在帶有2個CPU內核的Windows 7 64位上運行測試程序時,使用gcc(g ++)編譯器使用-fopenmp標誌並運行編譯後的程序與沒有運行程序相比沒有引起明顯的減速OpenMP支持。

做同樣的上Kubuntu的11.04 64位在同一計算機上,但是,所提出的執行到的非OpenMP的版本4倍以上。這個問題似乎只出現在Unix系統上,而不是Windows上。

我的測試程序的來源如下。我還上傳了win和unix版本的壓縮源代碼,以及win和unix版本的裝配源,以及是否支持OpenMP。此壓縮可以在這裏http://www.2shared.com/file/0thqReHk/omp_speed_test_2011_05_11.html

#include <stdio.h> 
#include <windows.h> 
#include <list> 
#include <sys/time.h> 
//#include <cstdlib> 

using namespace std; 

int main(int argc, char* argv[]) 
{ 
// #pragma omp parallel 
// #pragma omp single 
// {} 

    int start = GetTickCount(); 
    /* 
    struct timeval begin, end; 
    int usecs; 
    gettimeofday(&begin, NULL); 
    */ 
    list<void *> pointers; 

    #pragma omp parallel for default(shared) 
    for(int i=0; i< 10000; i++) 
    //pointers.push_back(calloc(20000, sizeof(void *))); 
    pointers.push_back(malloc(20000)); 

    for(list<void *>::iterator i = pointers.begin(); i!= pointers.end(); i++) 
    free(*i); 

    /* 
    gettimeofday(&end, NULL); 
    if (end.tv_usec < begin.tv_usec) { 
    end.tv_usec += 1000000; 
    begin.tv_sec += 1; 
    } 
    usecs = (end.tv_sec - begin.tv_sec) * 1000000; 
    usecs += (end.tv_usec - begin.tv_usec); 
    */ 

    printf("It took %d milliseconds to finish the memory operations", GetTickCount() - start); 
    //printf("It took %d milliseconds to finish the memory operations", usecs/1000); 

    return 0; 
    } 

下載什麼仍然沒有答案現在的問題是,我能做些什麼來避免的問題,如這些Unix的平臺上..

+1

這個很簡單。如果你使用的是標準的malloc,那麼它就是線程安全的,只是因爲它爲整個分配例程執行了鎖定。這基本上是對代碼進行序列化並添加鎖定開銷。如果你想在並行區域內使用malloc,那麼你應該研究一些可用的多線程環境更好的內存分配包(例如Hoard,ptmalloc,tcmalloc等)。請參閱主題:「多線程是否強調內存碎片?」 (http://stackoverflow.com/questions/5875989/does-multithreading-emphasize-memory-fragmentation)。 – ejd 2011-05-11 14:54:27

1

謝謝大家。我們通過與TCMalloc(提供的解決方案ejd之一)進行鏈接,能夠解決這個問題。執行時間立即下降,與非線程版本相比,我們的執行時間可以縮短約40%。我們使用了2個核心。在使用GCC的Unix上使用OpenMP時,您應該也選擇標準內存管理解決方案的替代品。否則,程序可能會放慢速度。

相關問題