这个问题:

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

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

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

编辑:我的答案是C而不是C ++,因为关于这个问题的确没有C ++的意思。

最佳答案

对于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


我还没有基准测试。

关于assembly - 用特定索引分配位的好方法是什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52632484/

10-11 03:24