8

我正在研究一个数据结构,其中有一个 16 个 uint64 的数组。它们在内存中的布局是这样的(下面每个代表一个 int64):

A0 A1 A2 A3 B0 B1 B2 B3 C0 C1 C2 C3 D0 D1 D2 D3

期望的结果是将数组转置为:

A0 B0 C0 D0 A1 B1 C1 D1 A2 B2 C2 D2 A3 B3 C3 D3

数组旋转 90 度也是未来循环可接受的解决方案:

D0 C0 B0 A0 D1 C1 B1 A1 D2 C2 B2 A2 D3 C3 B3 A3

我需要这个以便稍后快速操作箭头(用另一个 SIMD 行程顺序遍历它,一次 4 个)。

到目前为止,我已经尝试通过加载 A 的 4 x 64 位向量、位掩码和混洗元素并将其与 B 等进行 OR 运算,然后对 C 重复该操作来“混合”数据......不幸的是,这是数组中每段 4 个元素的 5 x 4 SIMD 指令(一个加载、一个掩码、一个随机播放、一个或带有下一个元素,最后是一个存储)。看来我应该可以做得更好。

我有可用的 AVX2,我用 clang 编译。

4

2 回答 2

11
uint64_t A[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
__m256i row0 = _mm256_loadu_si256((__m256i*)&A[ 0]); //0 1 2 3
__m256i row1 = _mm256_loadu_si256((__m256i*)&A[ 4]); //4 5 6 7
__m256i row2 = _mm256_loadu_si256((__m256i*)&A[ 8]); //8 9 a b
__m256i row3 = _mm256_loadu_si256((__m256i*)&A[12]); //c d e f

我现在没有硬件来测试它,但是像下面这样的东西应该可以做你想要的

__m256i tmp3, tmp2, tmp1, tmp0;
tmp0 = _mm256_unpacklo_epi64(row0, row1);            //0 4 2 6
tmp1 = _mm256_unpackhi_epi64(row0, row1);            //1 5 3 7
tmp2 = _mm256_unpacklo_epi64(row2, row3);            //8 c a e
tmp3 = _mm256_unpackhi_epi64(row2, row3);            //9 d b f
//now select the appropriate 128-bit lanes
row0 = _mm256_permute2x128_si256(tmp0, tmp2, 0x20);  //0 4 8 c
row1 = _mm256_permute2x128_si256(tmp1, tmp3, 0x20);  //1 5 9 d
row2 = _mm256_permute2x128_si256(tmp0, tmp2, 0x31);  //2 6 a e
row3 = _mm256_permute2x128_si256(tmp1, tmp3, 0x31);  //3 7 b f

__m256i _mm256_permute2x128_si256 (__m256i a, __m256i b, const int imm)

内在从两个源中选择 128 位通道。您可以在Intel Intrinsic Guide中了解它。有一个版本_mm256_permute2f128_si256只需要 AVX 并在浮点域中运行。我用它来检查我是否使用了正确的控制字。

于 2014-11-19T10:28:09.740 回答
4

另一种方法是使用收集指令,您可以直接加载转置矩阵。下面的五行代码在 i7-Haswell 上使用 gcc 是可以的。

  int32_t stride = 4 * sizeof(A[0]);
  __m128i r128_gather_idx = _mm_set_epi32(3 * stride, 2 * stride, 1 * stride, 0 * stride);
  __m256i row0 = _mm256_i32gather_epi64(reinterpret_cast<long long const *>(&A[ 0]), r128_gather_idx, 1);
  __m256i row1 = _mm256_i32gather_epi64(reinterpret_cast<long long const *>(&A[ 1]), r128_gather_idx, 1);
  __m256i row2 = _mm256_i32gather_epi64(reinterpret_cast<long long const *>(&A[ 2]), r128_gather_idx, 1);
于 2014-11-20T10:21:15.593 回答