2011-01-21 77 views
2

我試圖爲訪問圖像像素編寫優化的代碼,並且需要爲超級快速創建一個for循環,而不必進入彙編級。更進一步索引是沿着行進行的,以最小化緩存未命中。什麼是c中最快的循環?

這是我有:

for (indr=0;indr<(height-1)*width;indr+=width) { 
     for (indc=0;indc<width;indc++){ 
      I[indr+indc]= dostuff ; 
     } 
    } 

我不能讓一個循環,因爲「dostuff」包括訪問在同一行的arent元素。

有沒有更快的方法來做到這一點?

編輯 好了,因爲我以前的帖子稍微不清楚我在這裏加入了完整的代碼。它的相當不可讀,但總的想法是,我用一個完整的圖像與一個簡單的盒子進行卷積。圖像首先在左側和底部用ws + 1個零填充,右側和頂部用ws零填充。然後將它製成一個完整的圖像Ii。以下函數採用積分圖像,並提取結果Ic與原始圖像大小相同的卷積。

void convI(float *Ic,float *Ii,int ws, int width, int height) 
{ 
    int W=width+ws*2+1,indR; 
    int H=height+ws*2+1,indC; 
    int w=width, indr; 
    int h=height, indc; 
    int jmpA=W*(ws+1),jmpC=W*ws,jmpB=ws+1,jmpD=ws; 

    for (indR=W*(ws+1),indr=0;indr<width*(height-1);indR+=W,indr+=width) { 
     for (indC=ws+1,indc=0;indc<width;indC++,indc++){ 
      //Performs I[indA]+I[indD]-I[indB]-I[indC]; 
      Ic[indr+indc]= 
      Ii[indR-jmpA+indC-jmpB]+ 
      Ii[indR+jmpC+indC+jmpD]- 
      Ii[indR+jmpC+indC-jmpB]- 
      Ii[indR-jmpA+indC+jmpD]; 
     } 
    } 
} 

這就是「dostuff」部分。循環緩慢。

+2

循環總是比訪問內存快。顯示你的「dostuff」代碼,或告訴我們它讀取的是什麼內存。 – BatchyX 2011-01-21 13:41:22

回答

6

如果你有所有的優化級別,沒有太多的理由認爲其他代碼會導致比你給出的更好的性能。

爲什麼你懷疑循環本身是一個瓶頸?如果不知道自己在做什麼,就沒有多少可以說的。對你的代碼進行基準測試,看看如果你有疑問就會產生這樣的彙編程序。

編輯:顯示了循環的內部部分之後。

在循環之外儘可能多地將索引計算的表達式放在一些位置。由於它與循環變量混合在一起,因此可能無法像應該那樣優化。 (或者只是對索引的計算進行重新排序,以便編譯器可以看到它,並且可能會盡可能多地進行預計算。)

最有可能是性能困難來自您的向量的訪問。如果您設法更好地計算索引,這也可能會得到改善,因爲編譯器/系統實際上會看到您以常規模式訪問您的向量。

如果這沒有幫助,請重新組織您的循環,以便向量的負載是遞增的而不是存儲。負載總是要等到數據在那裏才能執行操作,而存儲則不那麼明智。

1

除非你想使用像SSE這樣的向量化指令,否則沒有太多可以完成的事情。

+0

我這樣做。我怎麼做?我正在使用iphone 4. – twerdster 2011-01-21 13:20:45

0

在循環之前可能會將外環中的height-1解除爲一個賦值。但是,那麼,我懷疑現在的普通編譯器會作爲標準優化來做到這一點。也可能是有另一個指針,設置爲I [indr],然後索引可能是10是一個小勝。

這兩個都需要一些非常小心的基準來記錄。

1

你看起來不錯。如果你想避免組裝,最好簡單的循環。 GCC很聰明。如果你清楚你想要你的代碼在做什麼,它通常會優化它。但是,如果您在生產代碼中使用了一些不常見的技巧,則可能無法推斷出您的「真正意義」。

根據什麼dostuff實際上做了,你可能會在臨時查找緩存I[indr+indc]一些勝利使你的代碼看起來像......

char t = I[indr+indc]; 
// do stuff 
I[indr+indc] = t; 

該代碼將不執行更糟(我假設你有至少基本的優化打開),但它可能會更好,如果你的do stuff足夠幻想(我可以詳述如果你想)。

而不要聽其他人提出簡單的數學循環。真的沒有必要。如果你看看在-O1生成的程序集,你會發現每次都會爲你完成。這是最便宜的優化之一。

2

您可以展開最內層的循環。您將失去可讀性,但CPU的緩存和預取隊列將會做得更好。雖然這總是如此,但我不知道你會獲得多少速度。 您可以聲明indcindr作爲寄存器變量,並嘗試避免重新計算(height-1)*width,而是將其保留在臨時變量中。要知道,乘法吃了很多的時鐘週期...

0
// DragonLord style: 
float *ic_p = I + (width * height) - 1; // fencepost 
// Start at the end, and work backwards 
// assumes I is 0-based and wraps, is contiguous 

for (indr=(height -1) * width; indr>=0; indr-=width) { 
// Sadly cannot test on indr -= width here 
// as the 0 pass is needed for the loop 
     for (indc=width; indc--;){ 
     // Testing on postdecrement 
     // allows you to use the 0 value one last time before testing it FTW 
      // indr and indc are both 0-based inside the loop for you 
      // e.g. indc varies from (width-1) down to 0 
      // due to postdecrement before usage 
      printf("I[ %d + %d ] == %f \n", indr, indc, *ic_p); 
      // always use pointers in C/C++ for speed, we are not Java 
      *ic_p-- = dostuff ; 
     } 
    } 

性能可以通過向0高度遞減計數來略有改善,如果你不需要在循環中INDR使用,或代替predecrementing postdecrementing的indc如果你可以通過基於1的indc來獲得,在這種情況下,indc應該初始化爲(寬度+1):

for (indc=(width+1); --indc;){ 
相關問題