2014-05-19 54 views
2

我正在寫一個程序,其中一堆不同的類,所有存儲在向量中,使用公共數據結構在私有成員上執行並行操作。我想爲使用OpenMP的多個處理器並行化它,但是我有兩個有關代碼中兩個操作的問題,下面的示例在下面的註釋中指出了這兩個問題,它們顯示了程序邏輯的簡化形式。是隻讀的全局變量和寫入私人類成員在openmp中的「虛假共享」

#include <omp.h> 
#include <iostream> 
#include <sys/timeb.h> 
#include <vector> 

class A { 
    private : 
    long _i; 
    public : 
    void add_i(long &i) { _i += i; } 
    long get_i() const { return _i; } 
}; 

int main() 
{ 
    timeb then; 
    ftime(&then); 
    unsigned int BIG = 1000000; 
    int N = 4; 
    std::vector<long> foo(BIG, 1); 
    std::vector<A *> bar; 
    for (unsigned int i = 0; i < N; i++) 
    { 
    bar.push_back(new A()); 
    } 
    #pragma omp parallel num_threads(4) 
    { 
    for(long i = 0; i < BIG; i++) 
    { 
     int thread_n = omp_get_thread_num(); 
     // read a global variable 
     long *to_add = &foo[i]; 
     // write to a private variable 
     bar[thread_n]->add_i(*to_add); 
    } 
    } 
    timeb now; 
    ftime(&now); 
    for (int i = 0; i < N; i++) 
    { 
    std::cout << bar[i]->get_i() << std::endl; 
    } 
    std::cout << now.millitm - then.millitm << std::endl; 
} 

第一條評論涉及從全局foo讀取。這是「虛假分享」(或數據晃盪)?我讀的大部分資源都是關於寫入操作方面的虛假共享,但我不知道讀取操作是否也適用。

第二個註釋解決了對bar中的類的寫入操作。同樣的問題:這是錯誤的分享?他們正在寫入同一個全局數據結構中的元素(這是我讀過的,晃盪),但只是在元素內部對私有數據進行操作。

當我與一個for循環替換OpenMP的宏,該程序是由約25%的速度,所以我猜我做錯了什麼......

+0

你的例子很奇怪。你說過一堆不同的課程,但我認爲你是指同一班級的一堆不同的對象?而你的意思是四個。這不是很多。但它也被設置爲似乎任意的線程數。你爲什麼不向我們展示你的非並行代碼,然後我們可以告訴你如何最好地並行化它。 –

回答

0

你最大共享問題是bar[thread_n]foo[i]的閱讀不成問題。

編輯:由於bar[thread_n]是持有指針,指針是什麼得到更新,很少或沒有共享。您仍然可以從每個CPU內核中加載「一次一塊」,而不是從每個緩存行讀取每個CPU內核的一個或兩個項目。所以下面的代碼仍然可以受益。一如既往,當它是一個性能問題時,基準測試(啓用優化)很多,因爲不同的系統會有不同的表現(取決於編譯器,CPU架構,內存子系統等)

這會更好地「 「在每個線程中一次一些項目。可能是這樣的:

const int STEP=16; 

for(long i = 0; i < BIG; i+=STEP) 
{ 
    int thread_n = omp_get_thread_num(); 
    int stop = std::min(BIG-i, STEP); // Don't go over edge. 
    for(j = 0; j < stop; j++) 
    { 
    // read a global variable  
    long *to_add = &foo[i+j]; 
    // write to a private variable 
    bar[thread_n*STEP + j]->add_i(*to_add); 
    } 
} 

您可能需要調整「STEP」以使其達到合適的水平。

+0

他的酒吧對象是堆上的「新」。誰知道他們最終在哪裏。在我的筆記本電腦上,分配粒度爲32字節,高速緩存行爲64.兩個對象可能會在同一高速緩存行中結束。 – Anycorn

+0

但是矢量'bar'是一段連續的記憶。 –

+0

這是指針矢量,'矢量'而不是'矢量' – Anycorn

1

現代內存分配器是線程感知的。也

const int N = 4; 
std::vector<long> foo(BIG, 1); 
std::vector<A *> bar(N); 
#pragma omp parallel num_threads(N) 
{ 
    int thread_n = omp_get_thread_num(); 
    bar[thread_n] = new A(); 
    for(long i = 0; i < BIG; i++) 
    { 
    // read a global variable 
    long *to_add = &foo[i]; 
    // write to a private variable 
    bar[thread_n]->add_i(*to_add); 
    } 
} 

注意,在這種情況下omp_get_thread_num()被稱爲:爲防止假共享,當涉及到修改每個實例的class A指向的bar的元素,你應該將並行區域內的內存分配,例如,只有一次,而不是您的代碼中的BIG次。調用一個函數的開銷相對較低,但是當你多次執行時,它會加起來。

+0

我不認爲''我是這種情況,並行區域內聲明的變量是自動私有的。 – Anycorn

+0

已修復。我有點錯過了在循環結構中聲明'i'的事實。 –