2012-05-23 136 views
3

我們有一個賦值給我們一個非常低效的程序,我們必須優化代碼以使其運行得更快。除了這兩個,我大部分都非常快,除了這兩個,我非常擔心,因爲它們非常簡單。一個基本上將二維數組中的所有值設置爲相同的值,並且基本上交換兩個二維數組的值。這就是問題所在。他們佔用了大部分時間,但因爲它們非常簡單,我無法弄清楚如何在不破壞功能的情況下減少它們。而且我知道可以讓他們跑得更快,因爲班上的其他學生一直在變得荒謬的加速。這兩個問題是:優化嵌套循環

fSetArray(int rows, int cols, float val) 
{ 
    int i, j; 
    F2D *out; 
    out = fMallocHandle(rows, cols); 

    for(i=0; i<rows; i++) 
    for(j=0; j<cols; j++) 
     subsref(out,i,j) = val; 

    return out; 

} 

唯一重要的部分是那裏的兩個循環。基本上,我們有一個具有一定寬度(行)和一定高度(cols)的二維數組,我們將所有值設置爲val。但是我無法消除其中一個循環(這將是加速它的最佳方式)。除非我錯過了一些明顯的東西。如果cols和rows是相同的數字,這會容易得多。

的另一個功能是:

fDeepCopy(F2D* in) 
{ 
    int i, j; 
    F2D* out; 
    int rows, cols; 

    rows = in->height; 
    cols = in->width; 

    out = fMallocHandle(rows, cols); 

    for(i=0; i<rows; i++) 
    for(j=0; j<cols; j++) 
     subsref(out,i,j) = subsref(in,i,j); 

    return out; 
} 

的出數組總是比數組較大,所以我們不必擔心溢出或此類。這個函數基本上只是交換了兩個數組的值,但再一次,因爲它非常簡單,我不能完全理解它。

而且有人說並行,因爲樣本量和服務器,創建線程所需的開銷,其實之前減慢程序。我試過了。由於這兩個函數太短,所以不值得創建線程才能在一個並行之後殺死它們。

但供參考,是在技術上這是一個OpenMP的項目,我們應該使用並行,但對於這兩個函數,這樣做實際上會減慢整個程序。我用一個並行for循環來檢查。

編輯:感謝大家誰給我出出主意!我已經掌握了它,並且全速奔跑!

+2

嘗試交換循環順序 - 它可能會影響緩存命中率。 – dasblinkenlight

+0

如何定義subsref?這很可能是一個宏,但如果它是一個函數,確保它被內聯。或將其重寫爲宏。 –

+0

您需要包含更多細節,特別是關於F2D結構和子參數函數。 – betabandido

回答

1

一類是循環展開。有時管道需要停頓,因爲在獲取索引,更新索引以及將其存儲回內存方面有很多活動。這可能是主要原因,你的多線程執行沒有做好,所有可能進行爭奪訪問索引的線程。

如果你想重新嘗試多線程的實現,讓每個線程知道它的「抵消」的基礎上的線程數,並讓每個線程處理,通過模數師

thread 0 works on i*rows+j % (thread count) = 0 
thread 1 works on i*rows+j % (thread count) = 1 
(and so on) 

發現了一個不同的餘數。如果你不這樣做想要重新嘗試多線程實現,仍然有一些技術可以提高性能。有時候,即使是單個線程也可能不會導致索引變量空轉(因爲它會導致處理器流水線的使用不足)。要嘗試修復這類問題,請將索引增加一個特定的數字,並在循環內調用該方法的次數。

fDeepCopy(F2D* in) 
{ 
    int i, j; 
    F2D* out; 
    int rows, cols; 

    rows = in->height; 
    cols = in->width; 

    out = fMallocHandle(rows, cols); 

    for(i=0; i<rows; i++) { 
     // rewrite to ensure we don't walk off "4 long" pads 
     int j = 0; 
     int pads = (cols/4)*4; 
     for(; j < pads; j = j + 4) { 
     subsref(out,i,j) = subsref(in,i,j); 
     subsref(out,i,j+1) = subsref(in,i,j+1); 
     subsref(out,i,j+2) = subsref(in,i,j+2); 
     subsref(out,i,j+3) = subsref(in,i,j+3); 
     } 
     // process the remainders 
     for(; j < pads; j++) { 
     subsref(out,i,j) = subsref(in,i,j); 
     } 
    } 
    return out; 
} 

現在CPU的設計師也越來越好,所以它是關鍵你實際配置您的運行,以確定是否已經CPU這樣做優化的,或者這種技術實際上可能你的代碼變慢。

最後,您還可以在leverage SSE extensions下,在正確的條件下,可以對所有存儲在MMX寄存器中的多個值執行相同的操作。例如,可以使用內聯的內聯將兩組四個32位單精度浮點數打包到MMX寄存器中,並且一次執行四個浮點的批量添加。

因此,一些看起來像

vec_res.x = v1.x + v2.x; 
vec_res.y = v1.y + v2.y; 
vec_res.z = v1.z + v2.z; 
vec_res.w = v1.w + v2.w; 

這需要八個內存查找,四種添加和四家門店(16個操作未優化)可能與

movaps xmm0, [v1]   ;xmm0 = v1.w | v1.z | v1.y | v1.x 
addps xmm0, [v2]   ;xmm0 = v1.w+v2.w | v1.z+v2.z | v1.y+v2.y | v1.x+v2.x    
movaps [vec_res], xmm0 

只需要三個替代操作,未優化。

再次,關鍵是分析,所以你可以檢測瓶頸,然後調整你的代碼,使他們在最小的可接受的性能。

0

memset應該是第一個功能有幫助。優化的