2014-01-30 114 views
0

我需要實現約簡操作(對於每個線程,值應該存儲在不同的數組條目中)。但是,對於更多的線程,它運行得更慢。有什麼建議麼?OpenMP實現減少

double local_sum[16];. 
//Initializations.... 
#pragma omp parallel for shared(h,n,a) private(x, thread_id) 
for (i = 1; i < n; i++) { 
    thread_id = omp_get_thread_num(); 
    x = a + i* h; 
    local_sum[thread_id] += f(x); 
} 

回答

4

您正在經歷虛假分享的影響。在x86上,單個高速緩存行的長度爲64個字節,因此保留64/sizeof(double) = 8個數組元素。當一個線程更新其元素時,其運行的核心使用緩存一致性協議來使所有其他核心中的相同緩存線無效。當另一個線程更新其元素時,或者直接在緩存上進行操作時,其內核必須從上級數據緩存或主內存中重新加載緩存行。這大大減慢了程序的執行速度。

最簡單的解決方案是插入填充,從而將由不同線程訪問的數組元素分散到不同的高速緩存行中。在x86上,這將是7 double元素。因此,你的代碼應該是這樣的:

double local_sum[8*16]; 
//Initializations.... 
#pragma omp parallel for shared(h,n,a) private(x, thread_id) 
for (i = 1; i < n; i++) { 
    thread_id = omp_get_thread_num(); 
    x = a + i* h; 
    local_sum[8*thread_id] += f(x); 

}

不要忘記總結在最後的數組時,只需要每個8元(或初始化所有的數組元素爲零)。

+0

保留原始16元素數組並不是更好,而是在並行循環中使用私有局部部分和減少,然後在並行循環之外但在並行塊中填充16元素數組。這仍然有錯誤的共享,但影響可以忽略不計,因爲該數組只在每個線程中命中一次,而不是每次迭代一次,此外,您不必擔心NUMA系統上的頁面。 –

+1

這樣做會更好,但它不會具有教導OP關於虛假分享的教育價值。 NUMA相關的優化後來:) –

-1

您是否嘗試過使用縮小?

double global_sum = 0.0; 
#pragma omp parallel for shared(h,n,a) reduction(+:global_sum) 
for (i = 1; i < n; i++) { 
    global_sum += f(a + i* h); 
} 

Howerver可能有很多其他原因爲什麼它運行緩慢。例如,如果只有2個CPU內核等,則不應創建16個線程。

+1

OP明確指出他必須使用數組實現約簡操作。 –