AVX512 中的人口数量

Population count in AVX512

我一直在尝试在支持 AVX512 的机器上以及之前针对 AVX2 优化过的代码上使用 _mm256_popcnt_epi64。

不幸的是,我 运行 遇到了找不到函数的问题。但是,找到了相应的 __m512i 等价物。 __m256i 函数是否已弃用?

_mm512_popcnt_epi64 is part of AVX512-VPOPCNTDQ。 256 位和 128 位版本还需要 AVX512VL 才能将 AVX512 指令与 128 位或 256 位向量一起使用。

主流的AVX512 CPU都有AVX512-VL。 Xeon Phi CPU 没有 AVX512-VL。

(_mm512_popcnt_epi8 和 epi16 在 Ice Lake 中也是新的,作为 AVX512-BITALG 的一部分)

也许您忘记启用必要的编译器选项(例如 GCC -march=native 以启用您正在编译的机器可以执行的所有操作),或者您正在为一个不具备两者的目标进行编译特征。如果是这样,那么编译器将不会将 _m256_popcnt_epi64 定义为内部函数,因此在 C 中它将假定其未声明的函数并发出对它的调用。 (当然不会在 link 时找到。)And/or 它将警告或错误(C 或 C++)关于未找到原型。

目前很少CPU有AVX512-VPOPCNTDQ (wikipedia AVX512 feature vs. CPU matrix):

  • Knight's Mill (final-generation Xeon Phi):只有 AVX512-VPOPCNTDQ,没有 AVX512VL,也没有 BITALG。因此只有 __m512i 版本可用于 gcc -O3 -march=knm。您绝对应该在 Xeon Phi 上使用 512 位向量,除非数据布局非常适合 256,并且需要额外的 512 位改组。但请注意,对于某些没有 512 位版本的 AVX / AVX2 指令,它的速度很慢,例如元素小于 32 位的随机播放。 (无 AVX512 BW)

  • Ice Lake / Tiger Lake:具有 AVX512 VPOPCNTDQ、BITALG 和 AVX512 VL,因此在编译此目标微体系结构时支持 _mm256_popcnt_epi64epi8,例如gcc -O3 -march=icelake-client。 (假设您的编译器的 headers 是正确的)。

    GCC8.3 和更早版本有一个错误,其中 -march=icelake-client / icelake-server 不启用 -mavx512vpopcntdq。 (GCC7 不知道 -march=icelake-client)。 GCC8.4已经修复了,所以要么升级到最新的GCC8,要么升级到最新稳定的GCC更好;再过几年的开发通常应该有助于 GCC 使用新的 ISA 扩展(如 AVX-512)编写更好的代码,尤其是掩码寄存器。或者只是手动使用-march=icelake-client -mavx512vpopcntdq;确实有效:https://godbolt.org/z/a7bhcjdhr


在 Ice Lake 上选择 256 位和 512 位向量是一种权衡,就像在 Skylake-x 上一样:当 512 位向量微指令运行时,端口 1 上的向量 ALU 不会被使用。并且最大涡轮时钟速度可能会降低。 SIMD instructions lowering CPU frequency。因此,如果您没有从更宽的向量中获得太多加速(例如,由于内存瓶颈,或者您的 SIMD 循环只是较大程序的一小部分),在一个循环中使用 512 位向量可能会损害整体性能。

但请注意,Icelake 客户端 CPU 没有受到太大影响,而且我不确定 vpopcnt 指令是否算作“繁重”,也许不会减少max turbo 尽可能多,如果在客户端 CPU 上。大多数整数 SIMD 指令都不算数。请参阅 LLVM [X86] Prefer 512-bit vectors on Ice/Rocket/TigerLake (PR48336) 上的讨论。不过,当 512 位微指令运行时,端口 1 的矢量 ALU 部分仍然关闭。


其他 CPU 根本不支持硬件 SIMD popcnt,并且 _mm512_popcnt_epi64 的形式不可用。

即使您只有 AVX2,根本没有 AVX512,SIMD popcnt 与标量 popcnt 相比,在具有快速 vpshufb 的现代 CPU 上的 non-tiny 数组上胜过 vpshufb (_mm256_shuffle_epi8). https://github.com/WojciechMula/sse-popcount/ 有 AVX2 和 AVX512 版本,使用 vpternlogd 进行 Harley-Seal 累加,以减少用于弹出计数的 SIMD LUT 查找量。

也在 Stack Overflow Counting 1 bits (population count) on large data using AVX-512 or AVX-2 上显示了一些几年前从该 repo 复制的代码。

如果您需要单独计算单独的元素,只需使用 vpshufbvpsadbw 的标准解包对零向量进行 hsum 到 64 位 qword 块。

如果你需要positional popcount(每个bit-position单独求和),见https://github.com/mklarqvist/positional-popcount