按位操作会影响性能

时间:2016-09-28 02:56:36

标签: c++ c assembly bitwise-operators

sys.exit()

HAS_BIT系列的组装是: enter image description here

我认为// 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 } } } 指令是and操作,所以它是否会如此昂贵?

PS:FLAG是0x2所以我猜编译器做了一些优化来为&生成一个指令。我用Vtune来描述。

2 个答案:

答案 0 :(得分: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;
        }
    }
}

答案 1 :(得分: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宏了。实际上&#34; has_all_bits&#34;测试

如果你打算只测试一个比特,或任何比特是好的,你应该尝试(这应该允许test使用):

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

即使我上面发布的代码更好地进行优化也可能有所帮助。

在FLAG固定为2的装配中,甚至可以将OR值计算为:

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