2012-07-13 20 views
3

我試圖通過並行化矩陣乘法來提高相當複雜的迭代算法的性能,矩陣乘法在每次迭代時被調用。該算法需要500次迭代和大約10秒。但在並行化矩陣乘法之後,它會減慢到13秒。然而,當我單獨測試相同維度的矩陣乘法時,速度有所增加。最後(我說的是100×100矩陣)使用openmp時出現奇怪的減速

,我關掉任何並行化算法內每個迭代添加下面的一段代碼,這也絕對沒有什麼想必不應該長時間:

int j; 

#pragma omp parallel for private(j) 

for (int i = 0; i < 10; i++) 
j = i; 

再次,與沒有這段代碼的相同算法相比,有30%的減速。因此,在主算法內部使用openmp調用任何並行操作500次會降低速度。這種行爲看起來很奇怪,任何人都有什麼線索是什麼問題?

主要算法由桌面應用程序調用,由VS2010,Win32 Release編譯。 我上英特爾酷睿(並行創建4個線程),64位Windows 7

在這裏工作是一個程序的結構:調用平行片 :

int internal_method(..) 

{ 
...//no openmp here 


// the following code does nothing, has nothing to do with the rest of the program and shouldn't take long, 
// but somehow adding of this code caused a 3 sec slowdown of the Huge_algorithm() 
double sum; 
#pragma omp parallel for private(sum) 
for (int i = 0; i < 10; i++) 
    sum = i*i*i/(1.0 + i*i*i*i); 

...//no openmp here 
} 


int Huge_algorithm(..) 
{ 

...//no openmp here 

    for (int i = 0; i < 500; i++) 
    { 
    .....// no openmp 

    internal_method(..); 

    ......//no openmp 
    } 

...//no openmp here 
} 

所以,最後一點是代碼500次(當省略算法的其餘部分時)的代碼時間不到0.01秒,但是當您在一個巨大的算法中調用500次時,它會導致整個算法延遲3秒。 而我不明白的是小的平行部分如何影響算法的其餘部分?

+0

只是爲了確定,你如何衡量執行時間?我在這裏看到很多OpenMP和MT相關的問題,在這些問題中,人們在並行程序中測量CPU時間而不是掛鐘時間。其他的事情是:進入和退出並行區域相對昂貴(即使現代化的OMP運行時池)。 – 2012-07-13 10:15:03

+0

我正在使用掛鐘時間,桌面應用程序實際測量時間。關於進入和退出平行區域是這樣的,但我有500個進入和退出以及3秒的放緩 - 這不加起來。 – Sergei 2012-07-13 10:27:59

+0

@ phresnel是對的。 – 2012-07-13 10:48:22

回答

0

也許只是j = i對於核心cpu帶寬來說並不是高產量。也許你應該嘗試更多的計算。 (exapmle考慮我*我*我*我*我*我除以我+我+我)

你在多核cpu或gpu上運行此?

+0

多核cpu(4核心)我只是不明白爲什麼一個微不足道的openmp並行化代碼會導致如此巨大的放緩。我發現使用分析器,有其他方法放緩與並行 – Sergei 2012-07-13 10:13:00

+0

沒有任何關係,你可以嘗試矩陣乘法與3計算的額外功率,並告訴加速的差異? – 2012-07-13 10:16:44

+0

@ user1523105,在StackOverflow上有大量與OpenMP性能相關的問題。如果沒有向我們展示實際算法中的一些示例代碼,它們都不能被回答。我會說,你還應該使用像英特爾線程檢查器這樣的工具來運行你的代碼,並尋找緩存問題,如錯誤共享。 – 2012-07-13 10:25:57

2

對於10次迭代和一個簡單的賦值,我猜與計算本身相比,OpenMP的開銷太多了。這裏看起來很輕量級,實際上是管理和同步多個線程,這些線程甚至可能不是來自線程池。可能會涉及到一些鎖定問題,我不知道MSVC在估計是否需要並行化方面有多棒。

嘗試更大的循環體或更大量的迭代(例如1024 * 1024迭代,僅適用於初學者)。


實施例的OpenMP Magick:

​​

這可能是大約由編譯器擴展爲:

const unsigned __cpu_count = __get_cpu_count(); 
const unsigned __j = alloca (sizeof (unsigned) * __cpu_count); 
__thread *__threads = alloca (sizeof (__thread) * __cpu_count); 
for (unsigned u=0; u!=__cpu_count; ++u) { 
    __init_thread (__threads+u); 
    __run_thread ([u]{for (int i=u; i<10; i+=__cpu_count) 
          __j[u] = __i;}); // assume lambdas 
} 

for (unsigned u=0; u!=__cpu_count; ++u) 
    __join (__threads+u); 

__init_thread()__run_thread()__join()在於調用非平凡函數某些系統調用。

在使用線程池的情況下,您可以用__pick_from_pool()左右等替換第一個alloca()

(注意這個,名稱和代碼發射,一切都是虛構的,實際執行情況將有所不同)


關於你更新的問題:

你似乎在錯誤的粒度要並行。把儘可能多的工作量儘可能在一個線程,所以不是

for (...) { 
    #omp parallel ... 
    for (...) {} 
} 

嘗試

#omp parallel ... 
for (...) { 
    for (...) {} 
} 

憑經驗:保持工作負載足夠大的每個線程,從而減少相關的開銷。

+0

MSVC使用線程池實現OpenMP線程團隊,但開銷仍然很大。 – 2012-07-13 10:54:36

+0

@Hristo Iliev:有趣的信息。你有沒有進一步的鏈接關於MSVC的實現細節? – 2012-07-13 10:56:40

+0

@ phresnel,謝謝。並行10次簡單的j = i或sum = i * i * i /(1.0 + i * i * i * i)顯然不是改善性能的方法。但我的觀點是,如何調用一個非常短的並行循環(它什麼也不做,與算法的其他部分無關)可能導致整個程序的3秒放緩? – Sergei 2012-07-13 11:07:54

相關問題