【发布时间】:2017-07-18 18:55:31
【问题描述】:
我有一个按行排列的浮点数数组(~20 cols x ~1M 行),我需要从其中一次提取两列到两个 __m256 寄存器中。
...a0.........b0......
...a1.........b1......
// ...
...a7.........b7......
// end first __m256
一种天真的方法是
__m256i vindex = _mm256_setr_epi32(
0,
1 * stride,
2 * stride,
// ...
7 * stride);
__m256 colA = _mm256_i32gather_ps(baseAddrColA, vindex, sizeof(float));
__m256 colB = _mm256_i32gather_ps(baseAddrColB, vindex, sizeof(float));
但是,我想知道是否可以通过在一个gather 中检索a0, b0, a1, b1, a2, b2, a3, b3 和在另一个a4, b4, ... a7, b7 中获得更好的性能,因为它们在内存中更接近,然后对它们进行去交错。那就是:
// __m256 lo = a0 b0 a1 b1 a2 b2 a3 b3 // load proximal elements
// __m256 hi = a4 b4 a5 b5 a6 b6 a7 b7
// __m256 colA = a0 a1 a2 a3 a4 a5 a6 a7 // goal
// __m256 colB = b0 b1 b2 b3 b4 b5 b6 b7
我不知道如何很好地交错lo 和hi。我基本上需要_mm256_unpacklo_ps 的反面。我想出的最好的方法是:
__m256i idxA = _mm256_setr_epi32(0, 2, 4, 6, 1, 3, 5, 7);
__m256i idxB = _mm256_setr_epi32(1, 3, 5, 7, 0, 2, 4, 6);
__m256 permLA = _mm256_permutevar8x32_ps(lo, idxA); // a0 a1 a2 a3 b0 b1 b2 b3
__m256 permHB = _mm256_permutevar8x32_ps(hi, idxB); // b4 b5 b6 b7 a4 a5 a6 a7
__m256 colA = _mm256_blend_ps(permLA, permHB, 0b11110000); // a0 a1 a2 a3 a4 a5 a6 a7
__m256 colB = _mm256_setr_m128(
_mm256_extractf128_ps(permLA, 1),
_mm256_castps256_ps128(permHB)); // b0 b1 b2 b3 b4 b5 b6 b7
这是 13 个周期。有没有更好的办法?
(据我所知,prefetch 已经尽可能地优化了幼稚的方法,但是由于缺乏这些知识,我希望对第二种方法进行基准测试。如果有人已经知道这样做的结果是什么,请分享. 使用上面的去隔行方法,它比朴素的方法慢了大约 8%。)
编辑即使没有去隔行,“近端”聚集方法也比简单的恒定步幅聚集方法慢约 6%。我认为这意味着这种访问模式过于混淆了硬件预取,因此不值得进行优化。
【问题讨论】:
-
a0和b0相差多少?
-
@FackedDeveloper 在运行时在 0 到 19 之间变化(即任何一对列)。
-
FWIW,x86 上的当前收集实现不利用连续元素,即,没有来自相邻地址的负载合并 - 每个负载单独发送到负载端口,所以你可以' t 目前打破每个循环 2 次负载的障碍(即 8 次负载需要 4 次循环)。这在未来可能会改变:高性能聚集背后的想法之一是它检测元素何时相邻或重叠,并在这种情况下发出更少的负载。所以有一天你的第二个策略可能会更快。