2016-09-28 40 views
2
// nx_, ny_ is like 350 * 350 
#define IJ_REF(_i, _j) ((_j)*nx_+(_i)) 
#define HAS_BIT(_v, _bit)  (((_v) & (_bit)) == (_bit)) 

for (int ix = 0; ix < nx_; ++ix) {    // 0.019s 
    for (int iy = 0; iy < ny_; ++iy) {   // 0.716s 
     int32 r = IJ_REF(ix, iy);    // 0.548s 
     if (!HAS_BIT(image_[r], FLAG)) {  // 3.016s 
      int32 k = r * 4;     // 0.242s 
      pTex[k] = pTex[k + 1] = pTex[k + 2] = pTex[k + 3] = 255; // 1.591s 
     } 
    } 
} 

打HAS_BIT線的組裝: enter image description here表現在位運算

我猜and指令是&動作,所以它假設是如此昂貴?

PS:FLAG是0x2,所以我猜編譯器做了一些優化,爲HAS_BIT生成單個指令。我使用Vtune進行配置。

+2

你不應該用下劃線開始你的標識符。這些標識符是爲編譯器的實現保留的。 – PaulMcKenzie

+0

@PaulMcKenzie那些宏定義將在編譯前被替換,但你是對的,我不應該這樣做。 – zoujyjs

+1

和本身不應該是昂貴的,但也許你會混淆分支預測器的垃圾,特別是如果沒有真正的模式來判斷這種情況是否屬實。然後你花了很多時間等待管道沖洗。 –

回答

9

命中不是因爲您使用的是按位指令,而是因爲指令從內存中讀取 - 比使用寄存器的偏移計算更昂貴的操作。

代碼的問題是它不會連續讀取內存,因爲根據IJ_REF您的圖像按行存儲,但是您按列讀取它。

你應該能夠提高通過增加高速緩存的命中數的表現,如果你換你的循環順序:

for (int iy = 0; iy < ny_; ++iy) { 
    for (int ix = 0; ix < nx_; ++ix) { 
     int32 r = IJ_REF(ix, iy); 
     if (!HAS_BIT(image_[r], FLAG)) { 
      int32 k = r * 4; 
      pTex[k] = pTex[k + 1] = pTex[k + 2] = pTex[k + 3] = 255; 
     } 
    } 
} 
+0

在這種情況下,你不希望在前面的movzx行看到更大的時間? –

+2

@JasonC我不是100%確定探查器在這裏是否正確歸因於計時,但你說得對,讀指令在'movzx'行。 – dasblinkenlight

+0

沒錯,交換導致時間消耗降低50%。再次,編譯器可能不會在指令級別對perciselly進行計時。但總體時間也下降了40%-50%。 – zoujyjs

1

你可以分析這個變體(無分支)和後的結果?我很好奇......(可能會比較慢,如果您很少將這些255寫入pTex,由於此代碼將觸碰每個含有or的pTex字節)。

#include <string> 

#define IJ_REF(_i, _j) ((_j)*nx_+(_i)) 
#define HAS_BIT(_v, _bit)  (((_v) & (_bit)) == (_bit)) 

int main() 
{ 
    constexpr uint32_t ny_ = 350, nx_ = 350; 
    constexpr uint8_t FLAG = 2; 
    uint8_t image_[ny_*nx_]; 
    uint8_t pTex[ny_*nx_*4]; 

    // let access pTex by uint32_t directly 
    uint32_t *pTex32bPtr = reinterpret_cast<uint32_t *>(pTex); 

    // debug input 
    image_[IJ_REF(nx_-2, ny_-1)] = FLAG; 
    image_[IJ_REF(nx_-1, ny_-1)] = ~FLAG; 
    pTex32bPtr[IJ_REF(nx_-2, ny_-1)] = 0x12345678; 
    pTex32bPtr[IJ_REF(nx_-1, ny_-1)] = 0x12345678; 

    // prepare for loop 
    const uint32_t endOfs = ny_*nx_; 
    constexpr uint32_t pTexORValue[2] = {0, 0xFFFFFFFF}; 
    // loop trough all [x,y] values 
    for (uint32_t srcOfs = 0; srcOfs < endOfs; ++srcOfs) { 
     unsigned ORindex = !HAS_BIT(image_[srcOfs], FLAG); 
     // if you know FLAG is always 2, it can be: 
     // ORindex = image_[srcOfs]&2; with pTexORValue array: 
     // [3] = {0xFFFFFFFF, 0, 0}; 
     pTex32bPtr[srcOfs] |= pTexORValue[ORindex]; 
    } 

    // debug output 
    for (size_t i = IJ_REF(nx_-2, ny_-1) * 4; i < IJ_REF(nx_, ny_-1)*4; ++i) { 
     printf(" %02x", pTex[i]); 
    } 
} 

而且我有點納悶,爲什麼你的編譯器movzx edx + and edx,同時它可以做test byte ptr [eax+ecx],2代替。什麼是FLAG的類型?哦,現在我明白了,這是因爲你的宏爲HAS_BIT。它實際上是「has_all_bits」測試。

如果您計劃僅測試單個位,或任何-的位好,你應該嘗試(這應該允許test使用):

#define HAS_SOME_BIT(_v, _bits)  (((_v) & (_bits)) != 0) 

它可以幫助甚至是代碼我張貼以上要更好地優化。


而且在與FLAG固定爲2裝配它來計算或價值甚至會可能:

mov ebx,image_offset 
loop: 
    movzx eax,Image_[ebx] 
    ; copy bit 0x02 to all 32 bits 
    shl eax,30 
    sar eax,31 
    not eax  ; flip it to "not HAS_BIT" 
    or  pTex[ebx*4],eax 
    ...