Каков хороший способ присвоить бит конкретному индексу?

Этот вопрос:

Как вы устанавливаете, очищаете и переключаете один бит?

обсуждает три операции над конкретным битом в пределах большего значения, которые прямо соответствуют OR 1, AND 0 и XOR 1 на этом бите. Но что, если мы не знаем, что значение второго бита заранее? Что делать, если мы хотим выполнить назначение , где мы не знаем другой бит операнда во время компиляции? То есть, мы хотим установить один бит в определенной позиции бит-блок на значение нового несвязанного бита, предоставленного во время выполнения?

Я хотел бы иметь самую быструю возможную реализацию для этого, скажем, Intel x86_64 (и игнорирование векторизации, которое, я надеюсь, здесь не имеет значения). Кроме того, предположим, что для простоты тип битового блока - uint32_t .

Изменить: Сделал мой ответ C, а не C ++, так как на самом деле ничего не сказано об C ++.

Всего 1 ответ


Вот две возможные реализации для случая 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 (или, еще лучше, посмотрите на эту версию , которая является одним и тем же кодом, но разделена на несколько операторов 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

Я пока ничего не сравнил.


Есть идеи?

10000