我正在努力思考 _mm256_shuffle_pd 和 _mm256_permute_pd 内在函数的工作原理。 我似乎无法预测其中一项操作的结果。
首先,_mm_shuffle_ps 一切正常。我得到的结果是我所期望的。例如:
float b[4] = { 1.12, 2.22, 3.33, 4.44 };
__m128 a = _mm_load_ps(&b[0]);
a = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 1, 2));
_mm_store_ps(&b[0], a);
// 3.33 2.22 1.12 4.44
所以一切都在这里。现在我想用我目前在我的代码中使用的 __m256d 来尝试这个。 据我发现,_mm256_shuffle_ps/pd 内在函数的工作方式不同。
我的理解是控制蒙版被应用了两次。第一次在 128 位的前半部分,第二次在最后 128 位。 前两对控制位用于从第一个 vector 中选择(并将值存储在结果 vector 的第一个和第二个字以及第五个和第六个字中),而最高位对从第二个 vector 中选择。 例如:
float b[8] = { 1.12, 2.22, 3.33, 4.44, 5.55, 6.66, 7.77, 8.88 };
__m256 a = _mm256_load_ps(&b[0]);
a = _mm256_shuffle_ps(a, a, 0b00000111);
_mm256_store_ps(&b[0], a);
// 4.44 2.22 1.12 1.12 8.88 6.66 5.55 5.55
这里我期望(我实际得到)的结果是 { 4.44, 2.22, 1.12, 1.12, 8.88, 6.66, 5.55, 5.55 }
这应该按如下方式工作:
(对不起,我画得不好)。 对第二个 vector (在本例中再次为 a)使用最高的两对(因此 00 00)并填充缺失的空格也是如此。
我认为 _mm256_shuffle_pd 的工作方式相同。因此,如果我想要第一个 double,我必须移动 00 空格和 01 空格才能正确构造它。
例如:
__m256d a = _mm256_load_pd(&b[0]);
a = _mm256_shuffle_pd(a, a, 0b01000100);
_mm256_store_pd(&b[0], a);
// 1.12 1.12 4.44 3.33
我原以为这会输出 { 1.12, 1.12, 3.33, 3.33 }。 在我的脑海中,我从第一个 vector 中获取 00 01 ( 1.12 ) 和 00 01 { 3.33 },从第二个 vector 中获取相同的值,它们是相同的 vector 和所有 vector 。
我已经为控制掩码尝试了很多组合,但我无法理解它是如何使用的,也无法找到以我能理解的方式解释它的地方。
所以我的问题是:_mm256_shuffle_pd 是如何工作的?我怎样才能得到与 _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)) 相同的结果,有四个 double 和一个洗牌(如果可能的话)?
最佳答案
shufps
只需要 4 个元素的所有 8 位立即数,每个元素有 4 个可能的来源。因此它没有 256 位的增长空间,唯一的选择是在两个 channel 中复制相同的洗牌。
但是 128 位 shufpd
只有 2 个元素,每个元素有 2 个源,因此是 2 x 1 位。所以 AVX 版本总共使用 4 位,每条 channel 2 位。 (它不是车道交叉,所以它不如 128 位 shufps
强大。)
http://felixcloutier.com/x86/SHUFPD.html有完整的文档和图表,以及详细的伪代码。 Intel 的内在函数指南 _mm256_shuffle_pd
具有相同的伪代码。
AVX2 http://felixcloutier.com/x86/VPERMPD.html (_mm256_permute_pd
,又名 _mm256_permute4x64_pd
)是跨车道的,并且使用其立即数与 128 位 shufps
完全相同:四个 2 位选择器。
唯一的跨车道 2 源洗牌是 vperm2f128
(_mm256_permute2f128_pd
) , 直到 AVX512F 引入更精细的粒度 vpermt2pd
和 vpermt2ps
(以及等效的整数洗牌。
AVX1 没有任何粒度小于 128 位的交叉洗牌,甚至没有 1 源版本。如果你需要一个,你必须用 vinsertf128
或 vperm2f128
+ in-lane shuffles 构建它。
因此,与 128 位 vector 的 float
相比,AVX 将 3D vector 保留在 SIMD vector 中甚至更糟。 http://fastcpp.blogspot.com/2011/04/vector-cross-product-using-sse-code.html可能比标量快,但如果您为 SIMD 设计数据布局,则效果会差很多。
使用连续 x[]
、y[]
和 z[]
的单独数组,这样您就可以进行 4x 交叉无混洗的并行乘积,并利用 FMA 指令。使用 SIMD 并行处理多个 vector ,而不是加速单个 vector 。
请参阅 https://stackoverflow.com/tags/sse/info 中的链接, 特别是 https://deplinenoise.wordpress.com/2015/03/06/slides-simd-at-insomniac-games-gdc-2015/这很好地解释了数据布局问题,以及使用 SIMD 向量化循环的哪个级别。
关于c++ - shuffle/permute 内在函数如何为 256 位 pd 工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51746720/