simd - 找到绝对最小值的最短方法。两个数字并将其乘以其在 AVX 中的输入符号

标签 simd avx avx2

关于如何在没有乘法的情况下为 C 语言以下逻辑实现 AVX 的任何提示,

for(int i = 0;i<4096;i++)
{
   out[i] = sign(inp1[i])*sign(inp2[i])*min(abs(inp1[i]), abs(inp2[i])); 
}

//inp1, inp2 & out 为16位寄存器。

最佳答案

您的问题有一个非常简短(但不明显)的解决方案:

res = max(min(a,b), -max(a,b));

(所有最小/最大操作都是有符号的)

为了解释为什么会这样,首先让我们设置

A = min(a,b); B = max(a,b);

这基本上是对 a 进行排序和 b (并排除了 A>0 && B<0 的情况)。我们现在只需要区分 3 种情况:

A<0  && B<0:     res = -B 
A<0  && B>=0:    res = -min(-A, B) = max(A, -B)
A>=0 && B>=0:    res = A

幸运的是,第一种情况和最后一种情况也可以计算为max(A,-B) , 因为在第一种情况下 A < 0 < -B , 在最后一个案例中 -B <= 0 <= A .

或者,您可以询问(并相信)WolframAlpha .(不是很有帮助,因为它只会评估为真“假设 a 和 b 为正数”——尽管您可以绘制两个表达式之间的差异)


用 AVX2 实现(忽略加载和存储):

__m256i A = _mm256_min_epi16(a,b);
__m256i B = _mm256_max_epi16(a,b);
__m256i res = _mm256_max_epi16(A, _mm256_sub_epi16(_mm256_setzero_si256(), B));

setzero操作将发生在任何循环之外,因此对于每个数据包,有三个最小/最大操作和一个 psub 操作。在 Intel-CPU 上,首先在端口上执行 p01 , 而 psub在任何 p015 上执行,因此循环会在 p01 上出现瓶颈,每个数据包需要 1.5 个周期。

如@Soonts 所述,-B操作可以溢出,对于 B=-0x8000 (有符号的 int16 没有正 0x8000)。这只发生在 a=b=-0x8000 上.如果你喜欢输出 0x7fff在这种情况下,您可以用饱和减法 ( _mm256_subs_epi16 ) 代替减法。

关于simd - 找到绝对最小值的最短方法。两个数字并将其乘以其在 AVX 中的输入符号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63995060/

相关文章:

C++ operator[] 访问 SIMD(例如 AVX)变量的元素

c++ - 将SSE矩阵 vector 乘法代码转换为AVX

x86 - 使用向量指令进行复杂的数据重组

c++ - 从模板参数中提取 simd vector 长度以用于本地类型

assembly - XNOR 8 位 block 中的两个 64 位寄存器

vectorization - "vectorization"是什么?

c++ - 如何启用/拱门:AVX for Unreal Engine 4?

c++ - 如何使用 Vector Class Library 进行 AVX 矢量化以及 openmp #pragma omp parallel 进行缩减?

c++ - 在 C++ SIMD 中将带符号的短整数转换为 float

assembly - 基于标量整数条件的 AVX 矢量寄存器的条件移动 (cmov)?