是否存在将AVX寄存器的64位组件的高/低32位组件重新打包到SSE寄存器中的固有或另一种有效方法?使用AVX2的解决方案是可以的。
到目前为止,我正在使用以下代码,但是分析器说 Ryzen 1800X 的速度很慢:
// Global constant
const __m256i gHigh32Permute = _mm256_set_epi32(0, 0, 0, 0, 7, 5, 3, 1);
// ...
// function code
__m256i x = /* computed here */;
const __m128i high32 = _mm256_castsi256_si128(_mm256_permutevar8x32_epi32(x),
gHigh32Permute); // This seems to take 3 cycles
最佳答案
在Intel上,您的代码将是最佳的。一条1-uop指令是最好的。 (除非您的输入 vector 是由vpermps
指令而非负载或其他东西创建的,否则您可能希望使用pd
来避免int/FP旁路延迟的任何风险。使用FP shuffle的结果作为整数指令的输入是通常在Intel上还可以,但是我不太确定将FP指令的结果提供给整数洗牌。)
尽管如果要针对Intel进行调优,则可以尝试更改周围的代码,以便可以将其改组为每个128b channel 的底部64位,以避免使用交叉 channel 的改组。 (然后,您可以只使用vshufps ymm
,或者如果要针对KNL进行调整,则可以使用vpermilps
,因为2输入vshufps
的速度较慢。)
在AVX512中,有一个 _mm256_cvtepi64_epi32
( vpmovqd
),它在 channel 之间打包了带有截断的元素。
在Ryzen上,过马路的洗牌很慢。 Agner Fog没有vpermd
的数字,但他列出了vpermps
(内部可能使用相同的硬件),其频率为3 oups,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));
在Intel上,使用3个改组而不是2个改组可为您提供2/3的最佳吞吐量,并为第一个结果带来1c的额外延迟。
关于c++ - 有效(在Ryzen上)将__m256的奇数元素提取到__m128的方法?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45867072/