关于如何在没有乘法的情况下为 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/