将 8 个 uint8_t 组合成一个 uintmax_t 的最快方法是什么?

What is the fastest way to combine 8 uint8_t into a single uintmax_t?

我希望优化一段使用 popcnt 来计算 uint8_t 之间差异的代码。我认为将 8 个 uint8_t 组合成一个 uintmax_t 并改为使用 popcnt64 会更快,这样 popcnt 操作不必被调用 8 倍以上。将 8 uint8_t 送入 popcnt64 的最快方法是什么?我可以使用某种铸件吗?我应该使用位操作吗?我不知道 C++ 的内部工作原理,所以我不确定进行此转换的最快方法是什么。

假设您不关心字节序——您只想将 uint8_t 视为 uint64_t 而您不关心 uint8_t 的顺序– 然后你可以只使用 std::memcpy 来做双关语:

std::uint64_t combine(std::array<std::uint8_t, 8> b) {
    static_assert(sizeof(b) == sizeof(std::uint64_t));
    static_assert(std::is_trivially_copyable_v<std::uint64_t>);
    static_assert(std::is_trivially_copyable_v<decltype(b)>);

    std::uint64_t result;
    std::memcpy(&result, b.data(), sizeof(result));
    return result;
}

generated assembly只是returns参数:

combine(std::array<unsigned char, 8ul>): # @combine(std::array<unsigned char, 8ul>)
  mov rax, rdi
  ret

使用任何其他类型双关使得你不得不担心严格的别名规则或类型对齐。只需使用 std::memcpy 并让编译器处理它就足够简单了


请注意,从 C++ 调用 popcnt 的任何变体的最简单方法是使用 std::bitset::count。因此,不用 __builtin_popcountll(my_u64)__popcnt64(my_u64),您只需编写 std::bitset<64>{my_u64}.count() 即可立即获得可移植代码。