2016-09-28 4 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% -ному снижению потребления времени. Опять же, компилятор может не указывать время на уровне директивы. Но общее время также снизилось на 40-50%. – zoujyjs

1

Вы можете профилировать этот вариант (без ветвления) и опубликовать результат? Мне любопытно ... (может быть, медленнее, если вы пишете те 255 в pTex редко, так как этот код будет касаться каждого байта pTex or).

#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 
    ...