计算 __mm256 向量中非零条目数的最快方法是什么?
What is the fastest way to count the number of nonzero entries in an __mm256 vector?
我编写了一种算法,使用 Intel 内部函数并行执行多个单精度运算。我的算法每次迭代的结果是单个 256 位向量 (__m256
) 中非零条目的数量。
例如:
00000000 FFFFFFFF 00000000 00000000 00000000 FFFFFFFF FFFFFFFF FFFFFFFF
其中迭代的结果是 4。
计算向量中非零条目数的最快方法是什么?
目前我正在做这样的事情:
float results[8];
_mm256_storeu_ps(results, result_vector);
int count = 0;
for (uint32_t idx = 0; idx < 8; ++idx)
{
if (results[idx] != 0)
{
++count;
}
}
这种方法工作得很好,但我想知道是否有更有效的方法,也许是不涉及商店的方法。
硬件 popcnt
指令是您最好的选择。它很快,并且 vmovmskps
也非常有效地为您提供每个元素的高位作为整数位掩码。 (compare / movemask 是对矢量比较结果进行分支的标准方法,或将其用于 )。
movemask / popcnt 很有用 ,可以根据存储的元素数量(洗牌后)增加目标指针。
#include <immintrin.h>
// use only with compare-results.
// or to count elements with their sign-bit set
unsigned count_true(__m256 v) {
unsigned mask = _mm256_movemask_ps(v);
return _mm_popcnt_u32(mask);
}
popcnt
有一个独立于 AVX 的功能位,所以理论上可能有一个 CPU(或虚拟机)带有 AVX 但不是硬件 popcnt
,但实际上我不会担心的。 (popcnt
是SSE4.2引入的,AVX隐含SSE4.2)
即使您希望将结果存储在矢量寄存器中,vmovmskps / popcnt / movd 的顺序也可能比使用整数加法水平添加 0
/ -1
元素更好。这将需要 3 shuffle/add 个步骤才能将 8 个元素减少到 1 个,并且您将得到一个负数。
我主要提到这一点是因为在某些情况下将比较结果视为整数 0
/ -1
很有用。例如要有条件地增加计数器向量,cmpps
/ psubd
就可以了。 (0 + x = x
,所以假元素不变。)
我编写了一种算法,使用 Intel 内部函数并行执行多个单精度运算。我的算法每次迭代的结果是单个 256 位向量 (__m256
) 中非零条目的数量。
例如:
00000000 FFFFFFFF 00000000 00000000 00000000 FFFFFFFF FFFFFFFF FFFFFFFF
其中迭代的结果是 4。
计算向量中非零条目数的最快方法是什么?
目前我正在做这样的事情:
float results[8];
_mm256_storeu_ps(results, result_vector);
int count = 0;
for (uint32_t idx = 0; idx < 8; ++idx)
{
if (results[idx] != 0)
{
++count;
}
}
这种方法工作得很好,但我想知道是否有更有效的方法,也许是不涉及商店的方法。
硬件 popcnt
指令是您最好的选择。它很快,并且 vmovmskps
也非常有效地为您提供每个元素的高位作为整数位掩码。 (compare / movemask 是对矢量比较结果进行分支的标准方法,或将其用于
movemask / popcnt 很有用
#include <immintrin.h>
// use only with compare-results.
// or to count elements with their sign-bit set
unsigned count_true(__m256 v) {
unsigned mask = _mm256_movemask_ps(v);
return _mm_popcnt_u32(mask);
}
popcnt
有一个独立于 AVX 的功能位,所以理论上可能有一个 CPU(或虚拟机)带有 AVX 但不是硬件 popcnt
,但实际上我不会担心的。 (popcnt
是SSE4.2引入的,AVX隐含SSE4.2)
即使您希望将结果存储在矢量寄存器中,vmovmskps / popcnt / movd 的顺序也可能比使用整数加法水平添加 0
/ -1
元素更好。这将需要 3 shuffle/add 个步骤才能将 8 个元素减少到 1 个,并且您将得到一个负数。
我主要提到这一点是因为在某些情况下将比较结果视为整数 0
/ -1
很有用。例如要有条件地增加计数器向量,cmpps
/ psubd
就可以了。 (0 + x = x
,所以假元素不变。)