用特定索引分配位的好方法是什么?

时间:2018-10-03 17:20:17

标签: assembly optimization bit-manipulation bitwise-operators micro-optimization

这个问题:

How do you set, clear, and toggle a single bit?

在较大值内的特定位上讨论三个操作,这些操作直接对应于该位上的OR 1,AND 0和XOR 1。但是,如果我们事先不知道第二位的值怎么办?如果我们要执行赋值,而又在编译时不知道其他操作数位,该怎么办?也就是说,我们想要在运行时提供的一个新的无关位的值上,在某个位置将一个位块设置为一位吗?

例如,我想在Intel x86_64上实现最快的实现(并且忽略向量化,我希望这里无关紧要)。另外,为简单起见,假设位块类型为uint32_t

编辑:我的回答是C而不是C ++,因为这个问题确实没有C ++的含义。

1 个答案:

答案 0 :(得分:2)

对于32位块类型,有两种可能的实现方式:

#include <cstdint>

uint32_t bit_assign_v1(uint32_t block, uint8_t bit_index, bool x)
{
    uint32_t mask = uint32_t { 1 } << bit_index;
    return (block & ~mask) | (((uint32_t) x) << bit_index);
}

uint32_t bit_assign_v2(uint32_t block, uint8_t bit_index, bool x)
{
    uint32_t mask = uint32_t { 1 } << bit_index;
    return x ? (block & ~mask) : (block | mask);
}

使用GodBolt,我为这两个选项中的每一个获得了不同优化的代码,随着我们更改平台和编译器,代码也有所不同。这是Skylake的example(或者更好的是,看看this version,它是相同的代码,但是分成更多的C语句,因此您可以更好地将程序集与C代码相关联。)

GCC 8.2程序集:

bit_assign_1:
        movzx   eax, sil
        btr     edi, eax
        movzx   edx, dl
        shlx    eax, edx, eax
        or      eax, edi
        ret
bit_assign_2:
        mov     ecx, 1
        shlx    esi, ecx, esi
        andn    eax, esi, edi
        or      esi, edi
        test    dl, dl
        cmove   eax, esi
        ret

clang 7.0程序集:

bit_assign_1:                           # @bit_assign_1
        btr     edi, esi
        shlx    eax, edx, esi
        or      eax, edi
        ret
bit_assign_2:                           # @bit_assign_2
        mov     eax, edi
        btr     eax, esi
        bts     edi, esi
        test    edx, edx
        cmovne  edi, eax
        mov     eax, edi
        ret

我还没有基准测试。