我有一个AVX寄存器,有四个双精度值。现在我需要在每个元素上单独执行一些算术。我需要做的是简化以下内容。
Situation:
a = [a4 a3 a2 a1]
w = [ 0 0 0 w1]
x = [ 0 0 0 x1]
y = [ 0 0 0 y1]
z = [ 0 0 0 z1]
Desired result:
w = [-- -- -- w1+a1]
x = [-- -- -- x1+a2]
y = [-- -- -- y1+a3]
z = [-- -- -- z1+a4]
除了期望的结果不仅是两个值的总和,而且是它们的更复杂的算术表达式。我放--
的地方,我的意思是我不关心那些将被丢弃的价值观。
我发现我可以使用置换操作来置换寄存器a
(参见例如Reverse a AVX register containing doubles using a single AVX intrinsic)。我唯一的问题是那些内在函数需要立即,即编译时的值,而我需要动态执行此操作。
我找到了对其他寄存器中包含的整数进行操作的置换内在函数,例如_mm256_permutevar_pd,但它们都没有在整个通道中进行置换(例如,使用这些指令就不可能首先使用a3
使用这些说明做我想做的事的唯一方法是使用if
,我宁愿避免。
我应该在128位通道上进行排列,这些通道受if
条件限制,然后在通道内进行动态排列吗?或者有更好的解决方案吗?我对性能和可维护性都很感兴趣。我可以使用最多AVX2指令。大会是一种选择,但我更喜欢内隐。
答案 0 :(得分:1)
理想情况下,在将+
打包到向量后,您可以执行[ z y x w ]
使用SIMD操作表示的任何内容。但如果没有:
以正常方式将所有4个元素提取为标量double
,然后执行任何操作:
void unpack_256_to_scalar(__m256d a) {
// unpack to two 128b halves
__m128d a01 = _mm256_castpd256_pd128(a); // extractf128_pd(a, 0) should also compile the same way, if you like more-consistent C instead of code that matches the asm you expect
__m128d a23 = _mm256_extractf128_pd(a, 1);
// and then halves of each 128b vector
double a0 = _mm_cvtsd_f64(a01);
double a1 = _mm_cvtsd_f64(_mm_unpackhi_pd(a01,a01));
double a2 = _mm_cvtsd_f64(a23);
double a3 = _mm_cvtsd_f64(_mm_unpackhi_pd(a23,a23));
...
// use the results
}
这个compiles (on the Godbolt compiler explorer)只有三个带有clang的指令,或者4个带有gcc的指令,因为它对寄存器分配是愚蠢的:
unpack_256_to_scalar(double __vector(4)):
vextractf128 xmm1, ymm0, 0x1
vunpckhpd xmm2, xmm0, xmm0
vmovapd xmm3, xmm1 # gcc should have use vunpckhpd xmm3, xmm1,xmm1. This wasted mov is a missed-optimization bug.
vunpckhpd xmm1, xmm1, xmm1
# the empty asm statement emitted the empty string here.
vzeroupper
ret
三个指令中的每一个都产生一个不同的元素作为其向量结果的低元素。不需要常量,甚至不需要立即常量(这就是为什么我选择unpackhi_pd
而不是shufpd
或vpermilpd
当clang从其内部表示中生成shuffle时使用的mysqldump
数据移动。)
使用需要向量作为控制掩码的变量shuffle在这里会很疯狂。没有任何相关内容似乎需要任何动态/变量随机播放或提取。
BTW,请参阅x86标记wiki,了解有关编写高性能代码的一些链接。