He3*_*xxx 9 c++ clang compiler-optimization avx2 avx512
我想将 a 转换bool[64]为 a uint64_t,其中每个位代表输入数组中元素的值。
在现代 x86 处理器上,这可以非常有效地完成,例如vptestmd与 AVX512 或vpmovmskbAVX256 一起使用。当我将 clang 的 bool 向量扩展与 结合使用时__builtin_convertvector,我对结果感到满意:
uint64_t handvectorized(const bool* input_) noexcept {
const bool* __restrict input = std::assume_aligned<64>(input_);
using VecBool64 __attribute__((vector_size(64))) = char;
using VecBitmaskT __attribute__((ext_vector_type(64))) = bool;
auto input_vec = *reinterpret_cast<const VecBool64*>(input);
auto result_vec = __builtin_convertvector(input_vec, VecBitmaskT);
return reinterpret_cast<uint64_t&>(result_vec);
}
Run Code Online (Sandbox Code Playgroud)
产生(Godbolt):
vmovdqa64 zmm0, zmmword ptr [rdi]
vptestmb k0, zmm0, zmm0
kmovq rax, k0
vzeroupper
ret
Run Code Online (Sandbox Code Playgroud)
但是,我无法让 clang (或 GCC 或 ICX)使用(可移植的)标量代码生成任何使用向量掩码提取的任何内容。对于这个实现:
vmovdqa64 zmm0, zmmword ptr [rdi]
vptestmb k0, zmm0, zmm0
kmovq rax, k0
vzeroupper
ret
Run Code Online (Sandbox Code Playgroud)
clang 生成 64*8B = 512B 查找表和 39 条指令。
这个实现以及我尝试过的其他一些标量实现(无分支、逆位顺序、使用)都可以在godboltstd::bitset上找到。它们都不会产生接近手写向量指令的代码。
有什么我遗漏的或者有什么原因导致优化在这里效果不佳吗?我可以编写一个标量版本来生成合理的矢量化代码吗?
我特别想知道,因为“handvectorized”版本不使用任何特定于平台的内在函数,并且实际上没有太多编程。它所做的只是“加载为向量”和“转换为位掩码”。也许 clang 根本没有检测到循环模式?这对我来说只是感觉很奇怪,一个简单的按位 OR 归约循环感觉像是一种常见模式,并且循环向量化器的文档明确列出了使用 OR 作为受支持功能的归约。
编辑:用评论中的建议更新了 godbolt 链接
Edit2:我刚刚意识到这个问题有一个开放的 LLVM 问题:https ://github.com/llvm/llvm-project/issues/41997