高效(在Ryzen上)将__m256的奇数元素提取到__m128中的方法?

时间:2017-08-24 16:49:33

标签: c++ vectorization x86-64 sse avx2

是否存在将AVX寄存器的64位组件的高/低32位组件重新打包到SSE寄存器的内在或其他有效方法?使用AVX2的解决方案还可以。

到目前为止,我正在使用以下代码,但是分析器说它在 Ryzen 1800X 上很慢:

this

1 个答案:

答案 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。