SIMD - AVX - 使用非零值而不是最高位进行屏蔽
SIMD - AVX - masking with non-zero value instead of highest bit
我有 AVX(没有 AVX2 或 AVX-512)。我有一个具有 32 位值的向量(仅使用 4 个最低位,其余始终为零):
[ 1010, 0000, 0000, 0000, 0000, 1010, 1010, 0000]
在内部,我将矢量保持为 __m256
,因为按位运算并且这些位表示 "float numbers"。我需要从向量中导出单个 8 位数字,其中 1 表示非零位,0 表示零位。
所以对于上面的例子,我需要 8 位数:10000110
我想使用 _mm256_cmp_ps
然后 _mm256_movemask_ps
。但是,对于 cmp,我不知道它是否会正常工作,如果数字不是完全浮点数并且可以是任何 "junk"。在这种情况下,哪个操作数用于 cmp?
或者有其他解决办法吗?
从概念上讲,您所做的应该有效。高 24 位为零的浮点数是有效的浮点数。然而,它们是非正规的。
虽然应该可以,但存在两个潜在问题:
- 如果 FP 模式设置为将非正规化刷新为零,那么它们都将被视为零。 (因此,打破了这种做法)
- 因为这些是非规范化的,所以您最终可能会遭受巨大的性能损失,具体取决于硬件是否可以原生处理它们。
替代方法:
由于高24位为零,可以归一化。然后做浮点比较
(警告:未经测试的代码)
int to_mask(__m256 data){
const __m256 MASK = _mm256_set1_ps(8388608.); // 2^23
data = _mm256_or_ps(data, MASK);
data = _mm256_cmp_ps(data, MASK, _CMP_NEQ_UQ);
return _mm256_movemask_ps(data);
}
此处,data
是您的输入,其中每个 "float" 的高 24 位为零。我们称这些 8 位整数中的每一个为 x
.
OR'ing with 2^23
设置浮点数的尾数,使其成为值为 2^23 + x
.
的规范化浮点数
然后您将 2^23
与 float
进行比较 - 只有当 x
不为零时才会给出 1。
备用答案,供将来 有 AVX2
的读者参考
您可以转换为 __m256i
并使用 SIMD 整数比较。
这避免了 DAZ 将这些小整数位模式视为恰好为零或微代码协助非正规(也称为次正规)输入的任何问题。
在某些 CPU 上 vcmpeqd
和 vpmovmskps
之间可能有 1 个额外的旁路延迟周期,但您仍然领先,因为整数比较的延迟低于 FP 比较。
int nonzero_positions_avx2(__m256 v)
{
__m256i vi = _mm256_castps_si256(v);
vi = _mm256_cmpeq_epi32(vi, _mm256_setzero_si256());
return _mm256_movemask_ps(_mm256_castsi256_ps(vi));
}
我有 AVX(没有 AVX2 或 AVX-512)。我有一个具有 32 位值的向量(仅使用 4 个最低位,其余始终为零):
[ 1010, 0000, 0000, 0000, 0000, 1010, 1010, 0000]
在内部,我将矢量保持为 __m256
,因为按位运算并且这些位表示 "float numbers"。我需要从向量中导出单个 8 位数字,其中 1 表示非零位,0 表示零位。
所以对于上面的例子,我需要 8 位数:10000110
我想使用 _mm256_cmp_ps
然后 _mm256_movemask_ps
。但是,对于 cmp,我不知道它是否会正常工作,如果数字不是完全浮点数并且可以是任何 "junk"。在这种情况下,哪个操作数用于 cmp?
或者有其他解决办法吗?
从概念上讲,您所做的应该有效。高 24 位为零的浮点数是有效的浮点数。然而,它们是非正规的。
虽然应该可以,但存在两个潜在问题:
- 如果 FP 模式设置为将非正规化刷新为零,那么它们都将被视为零。 (因此,打破了这种做法)
- 因为这些是非规范化的,所以您最终可能会遭受巨大的性能损失,具体取决于硬件是否可以原生处理它们。
替代方法:
由于高24位为零,可以归一化。然后做浮点比较
(警告:未经测试的代码)
int to_mask(__m256 data){
const __m256 MASK = _mm256_set1_ps(8388608.); // 2^23
data = _mm256_or_ps(data, MASK);
data = _mm256_cmp_ps(data, MASK, _CMP_NEQ_UQ);
return _mm256_movemask_ps(data);
}
此处,data
是您的输入,其中每个 "float" 的高 24 位为零。我们称这些 8 位整数中的每一个为 x
.
OR'ing with 2^23
设置浮点数的尾数,使其成为值为 2^23 + x
.
然后您将 2^23
与 float
进行比较 - 只有当 x
不为零时才会给出 1。
备用答案,供将来 有 AVX2
的读者参考您可以转换为 __m256i
并使用 SIMD 整数比较。
这避免了 DAZ 将这些小整数位模式视为恰好为零或微代码协助非正规(也称为次正规)输入的任何问题。
在某些 CPU 上 vcmpeqd
和 vpmovmskps
之间可能有 1 个额外的旁路延迟周期,但您仍然领先,因为整数比较的延迟低于 FP 比较。
int nonzero_positions_avx2(__m256 v)
{
__m256i vi = _mm256_castps_si256(v);
vi = _mm256_cmpeq_epi32(vi, _mm256_setzero_si256());
return _mm256_movemask_ps(_mm256_castsi256_ps(vi));
}