如果我有一个包含4个双精度浮点数的AVX寄存器,并且我想将其反转并储存在另一个寄存器中,是否可以使用单个内在命令来完成这个操作?
例如,如果我有一个包含4个浮点数的SSE寄存器,我可以使用:
_mm_shuffle_ps(A,A,_MM_SHUFFLE(0,1,2,3));
我可以使用_mm256_permute2f128_pd()
来实现吗?我认为你无法使用上述内部函数来访问每个独立的double。
如果我有一个包含4个双精度浮点数的AVX寄存器,并且我想将其反转并储存在另一个寄存器中,是否可以使用单个内在命令来完成这个操作?
例如,如果我有一个包含4个浮点数的SSE寄存器,我可以使用:
_mm_shuffle_ps(A,A,_MM_SHUFFLE(0,1,2,3));
我可以使用_mm256_permute2f128_pd()
来实现吗?我认为你无法使用上述内部函数来访问每个独立的double。
实际上您需要使用两次排列来完成此操作:
_mm256_permute2f128_pd()
只能在 128 位块中进行排列。_mm256_permute_pd()
不能跨越 128 位边界进行排列。因此,您需要同时使用这两种方法:
inline __m256d reverse(__m256d x){
x = _mm256_permute2f128_pd(x,x,1);
x = _mm256_permute_pd(x,5);
return x;
}
测试:
int main(){
__m256d x = _mm256_set_pd(13,12,11,10);
cout << x.m256d_f64[0] << " " << x.m256d_f64[1] << " " << x.m256d_f64[2] << " " << x.m256d_f64[3] << endl;
x = reverse(x);
cout << x.m256d_f64[0] << " " << x.m256d_f64[1] << " " << x.m256d_f64[2] << " " << x.m256d_f64[3] << endl;
}
输出:
10 11 12 13
13 12 11 10
支持粒度细于128位的车道交叉洗牌是AVX2中的新功能:
_mm256_permute4x64_pd(vec, _MM_SHUFFLE(0,1,2,3)); // i.e. 0b00011011
VPERMPD ymm1, ymm2/m256, imm8
在英特尔CPU上与其他跨通道洗牌操作(如VPERM2F128
)具有相同的吞吐量和延迟。此外,在内部函数查找器中也有相关信息。
vpermpd
比2输入的vperm2f128
更快。它们的矢量ALU内部仅为128位;256位矢量指令被解码为至少2个uops,但对于跨越通道的操作需要更多的uops,特别是可以读取任意4个总通道中的一个的操作。(不幸的是,解码器在选择vperm2f128的uops时不仅仅查看即时位)。在Bulldozer家族和Zen1上,手动vextractf128
/ vinsertf128
比vperm2f128
更好,但在其他地方会很糟糕。https://uops.info/。我认为vpermpd
在Excavator / Zen1上是最佳选择,3个uops vs.至少4个uops来进行通道内反转,然后使用vextracti128
/ vinsert128
交换两半部分。
有一些CPU支持FMA3但不支持AVX2,例如AMD Piledriver和Steamroller。在英特尔方面,AVX2和FMA都是Haswell时期的新技术。虽然AMD Bulldozer系列已经过时,但仍然存在于家用计算机中,因此即使您的函数利用了AVX1 + FMA,您的选择是要求使用AVX2并让那些少数CPU回退到更糟糕的情况(例如没有FMA的AVX1),或者再制作一个版本的函数。
VPERMPD
/_mm256_permute4x64_pd
。 英特尔的参考文献有更多细节。 - Stephen Canon