c++ - shuffle/permute 内在函数如何为 256 位 pd 工作?

标签 c++ intrinsics avx

我正在努力思考 _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 }

这应该按如下方式工作:

enter image description here

(对不起,我画得不好)。 对第二个 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 引入更精细的粒度 vpermt2pdvpermt2ps(以及等效的整数洗牌。

AVX1 没有任何粒度小于 128 位的交叉洗牌,甚至没有 1 源版本。如果你需要一个,你必须用 vinsertf128vperm2f128 + 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/

相关文章:

java - Java 和 C++ 之间的 IPC

c++ - 遍历所有长度为 `L` 的 vector

c - 如何用_mm_clflushopt函数编译程序?错误 : inlining failed

c++ - 在 ARM 上初始化 uint32x4_t 时出现错误 C2078?

c++ - 与 int 乘 int 相比,为什么执行浮点乘浮点矩阵乘法更快?

c++ - 递归地包含头文件以进行合成

c++ - C++11 中的泛型函数指针

sse - 使用 SSSE3 将 2 个未对齐的 64 位值加载到 sse 寄存器中的最佳方法是什么?

optimization - 128 位 SSE 计数器?

x86 - 如何找到 AVX 向量中元素的索引?