是否存在将AVX寄存器的64位组件的高/低32位组件重新打包到SSE寄存器的内在或其他有效方法?使用AVX2的解决方案还可以。
到目前为止,我正在使用以下代码,但是分析器说它在 Ryzen 1800X 上很慢:
this
答案 0 :(得分:4)
在英特尔上,您的代码将是最佳的。一次1-uop指令是你得到的最好的。 (如果您的输入向量是由vpermps
指令而不是负载或其他东西创建的,那么您可能希望使用pd
来避免int / FP旁路延迟的任何风险。使用FP的结果作为整数指令的输入,shuffle在Intel上通常很好,但是我不太确定将FP指令的结果提供给整数shuffle。)
虽然如果调整英特尔,您可能会尝试更改周围的代码,以便您可以随机播放到每个128b通道的底部64位,以避免使用通道交叉随机播放。 (那么你可以使用vshufps ymm
,或者如果调整为KNL,vpermilps
,因为2输入vshufps
较慢。)
使用AVX512,有_mm256_cvtepi64_epi32
(vpmovqd
)可以跨越通道打包元素,并进行截断。
在Ryzen,交叉的洗牌很慢。 Agner Fog没有vpermd
的数字,但是他列出了vpermps
(内部可能使用相同的硬件)3 uops,5c延迟,每4c吞吐量一个。
vextractf128 xmm, ymm, 1
对Ryzen非常有效(1c延迟,0.33c吞吐量),这并不奇怪,因为它已经将256b寄存器跟踪为两个128b两半。 shufps
也很有效(1c延迟,0.5c吞吐量),并且可以让你将两个128b寄存器混合到你想要的结果中。
这还可以为您不再需要的2个vpermps
随机屏蔽保存2个寄存器。
所以我建议:
__m256d x = /* computed here */;
// Tuned for Ryzen. Sub-optimal on Intel
__m128 hi = _mm_castpd_ps(_mm256_extractf128_pd(x, 1));
__m128 lo = _mm_castpd_ps(_mm256_castpd256_pd128(x));
__m128 odd = _mm_shuffle_ps(lo, hi, _MM_SHUFFLE(3,1,3,1));
__m128 even = _mm_shuffle_ps(lo, hi, _MM_SHUFFLE(2,0,2,0));
在英特尔上,使用3次shuffle而不是2次,可以获得最佳吞吐量的2/3,第一次结果的额外延迟为1c。