std :: bitset和SSE指令

kip*_*622 5 c++ sse bitset

是否可以在std :: bitset的基础数据上使用SSE指令?我正在使用的位集大于无符号长整数,因此to_ulong()方法是不够的.例如,我可以使用这样的指令:

__m128i* ptr= (__m128i*)(&my_bitset[0]);
Run Code Online (Sandbox Code Playgroud)

然后按正常情况执行SSE操作?

对于使用带有SSE的std :: bitset的人来说,我试图搜索互联网,但它似乎并不是一个常见的用例.

Max*_*kin 6

是否可以对 std::bitset 的基础数据使用 SSE 指令?

__m128i* ptr= (__m128i*)(&my_bitset[0]);
Run Code Online (Sandbox Code Playgroud)

my_bitset[0]返回未指定布局的临时代理对象,其中包含指向容器/存储的指针和位索引(例如GNU C++std::bitset::reference实现)。将指针转换为指向此临时代理对象是__m128i*没有意义的。但 C++ 根本不允许获取临时对象的地址,因此&my_bitset[0]会导致编译器错误。


std::bitset如果/当编译器选择对其进行向量化时,可以自动为其成员函数使用 SIMD 指令。

在此示例中,gcc 决定使用 AVX-256 指令,而 clang 决定不使用。这两种选择都不理想:

  • gcc 生成具有 256 位ymm寄存器的 AVX 指令,这会降低较旧的 Intel CPU 上的 CPU 频率(或使超频的 CPU 崩溃并强制 AVX 偏移量为 0)。ymm但向量大小太小,不足以证明在使用零星寄存器指令时付出增加 CPU 功耗和可能降低频率的代价是合理的。

  • clang 生成 64 位通用寄存器指令,与具有 128 位xmm寄存器的 SSE 相比,它需要更多的指令字节和更多的加载/存储。CPU 每个周期只能执行固定数量的加载/存储指令(而不是字节),因此最大化每条指令加载和存储的数据量是有意义的。

本例中的理想选择可能是使用具有 128 位xmm寄存器的 SSE 指令 - 最大限度地减少加载/存储指令的数量,而无需降低 CPU 的时钟频率。这表明编译器矢量化通常并不理想。


std::bitset不幸的是,它不提供对其存储的直接访问,并且通过 C 风格转换对其进行的任何访问都可能会导致未定义的行为,而不会由于布局、对齐或严格别名冲突而出现警告或错误。

std::bitset由于可移植性限制,不太可能使用任何非标准/SIMD 类型进行存储,因此将其存储转换为更广泛的 SIMD 类型几乎可以保证对齐和严格的别名冲突。有一些不可移植的方法可以解决这个问题,但这对于未来的变化来说很脆弱,这就是为什么我不建议这样做。


您可能希望寻找其他以 SIMD 设计的容器,例如Vc:可移植、零开销的 C++ 类型,用于显式数据并行编程。它允许选择在每个容器类的基础上使用的 SIMD 指令类型,例如,xmm即使有 256 位寄存器ymm可用,您可能只喜欢针对该特定容器类型使用 128 位寄存器指令。


gcc 和 clang 都支持通过内置函数对使用 声明的类型使用向量指令__attribute__((vector_size (N))),这是另一种方式:

目前,GCC 允许在这些类型上使用以下运算符:+、-、*、/、一元减号、^、|、&、~、%。

但这些不允许在每个容器类的基础上选择底层 SIMD 类型/指令,只能为每个具有编译器选项(如-mno-avx.

  • 当 AVX2 是新的(Haswell)时,*服务器*芯片往往会因使用 256 位向量而产生一些频率损失。如今,这个问题已经相当小了,只有 512 位向量在 Skylake-X 上造成了显着的频率损失,而在 IceLake 上即使对于 AVX-512 也没有太大的损失,至少在客户端芯片上是这样。请参阅 https://reviews.llvm.org/D111029 上有关 clang 至少为“-march=icelake-client”启用 512 位矢量化的讨论,其灵感来自于 https://travisdowns 上的 Ice-Lake 客户端频率表。 github.io/blog/2020/08/19/icl-avx512-freq.html。 (2认同)

Ale*_*iev 1

bitset没有访问其内部数据的标准方法。

itsy_bitsy库提供了与其他数据类似的接口bitsetbit_view是您所需要的,它包装具有操作位能力的数据,但没有insert/erase操作。

不确定是否可以bitsy::bit_view直接使用__m128i类型,但它支持 like bitsy::bit_view<std::span<char>>,因此您可以拥有__m128i变量并将其重新解释为chars 的范围,