最快的步幅 2 聚集
Posted
技术标签:
【中文标题】最快的步幅 2 聚集【英文标题】:fastest stride 2 gather 【发布时间】:2020-07-11 11:58:48 【问题描述】:我知道在使用 AVX2 进行快速步幅 3 收集时存在问题。我想知道最快的步幅 2 收集序列是什么,假设我想将长度为 16 向量的所有奇数元素加载到 ymm0
。
特别是,我想知道的相对收益和成本
-
使用步幅为 2 的 AVX2 聚集和
发出两个向量加载,然后使用一系列混合和随机播放指令。
如果 2) 总是比 1) 好,那么最好的指令顺序是什么?
【问题讨论】:
你能澄清一下元素的大小吗? 最佳解决方案将取决于周围代码的端口使用情况(也取决于您是否还需要偶数元素),但聚集不太可能是最有效的方式。 对于 32 位或 64 位的元素大小,256 位加载 +vshufps
+(vpermd
或 vpermq
)可能是最好的。
是否需要保留元素的相对顺序?或者,如果元素以其他一些但可预测的顺序出现,那也没关系?
大小为 32 位浮点数。我需要保留元素的相对顺序
【参考方案1】:
由于vshufps
和vpermps
都在端口5(英特尔Skylake)上执行,我更喜欢vblendps
+vpermps
而不是vshufps
+vpermps
,以获得更好的指令组合。在 intel skylake vblendps
上可以在端口 0、1 或 5 上执行。以下解决方案使用 2 个重叠向量加载:
#include <stdio.h>
#include <immintrin.h>
__m256 stide_2_load_odd(float * a)
__m256 x_lo = _mm256_loadu_ps(&a[1]);
__m256 x_hi = _mm256_loadu_ps(&a[8]);
__m256 x_b = _mm256_blend_ps(x_lo, x_hi, 0b10101010);
__m256 y = _mm256_permutevar8x32_ps(x_b, _mm256_set_epi32(7,5,3,1,6,4,2,0));
return y;
__m256 stide_2_load_even(float * a)
__m256 x_lo = _mm256_loadu_ps(&a[0]);
__m256 x_hi = _mm256_loadu_ps(&a[7]);
__m256 x_b = _mm256_blend_ps(x_lo, x_hi, 0b10101010);
__m256 y = _mm256_permutevar8x32_ps(x_b, _mm256_set_epi32(7,5,3,1,6,4,2,0));
return y;
int main()
float a[] = 0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 8.1, 9.1, 10.1, 11.1, 12.1, 13.1, 14.1, 15.1;
float b[8];
__m256 y = stide_2_load_odd(a);
_mm256_storeu_ps(b, y);
printf("odd indices 1, 3, 5, ...\n");
for(int i=0; i<8; i++)
printf("y[%i] = %f \n", i, b[i]);
y = stide_2_load_even(a);
_mm256_storeu_ps(b, y);
printf("\neven indices 0, 2, 4, ...\n");
for(int i=0; i<8; i++)
printf("y[%i] = %f \n", i, b[i]);
return 0;
输出是:
$gcc -Wall -O3 -march=skylake -o main *.c
$main
odd indices 1, 3, 5, ...
y[0] = 1.100000
y[1] = 3.100000
y[2] = 5.100000
y[3] = 7.100000
y[4] = 9.100000
y[5] = 11.100000
y[6] = 13.100000
y[7] = 15.100000
even indices 0, 2, 4, ...
y[0] = 0.100000
y[1] = 2.100000
y[2] = 4.100000
y[3] = 6.100000
y[4] = 8.100000
y[5] = 10.100000
y[6] = 12.100000
y[7] = 14.100000
这里使用了未对齐的负载。在现代 cpu 上,只要从内存中读取操作不跨越任何高速缓存行边界,这些都不会导致任何性能损失。因此,最好使用 64 字节对齐的地址a
调用这两个函数。另请参阅 Peter Cordes 的 comment。
【讨论】:
请注意vblendps
+vpermps
几乎肯定会比vgatherdps
快,请参阅chtz' 评论。
vshufps
允许对齐两个负载。但是,如果您知道相对于 64 字节对齐的位置,则可以通过在缓存行的开头将 _mm256_loadu_ps(&a[1]);
与 a
放在一起而不是重叠到下一个缓存行来避免缓存行拆分。然后它仍然是 Intel 的完整性能。
您想要一个 64 字节 对齐的 a
。如果它是 32 但 不是 64,您的第一次加载将缓存拆分。以上是关于最快的步幅 2 聚集的主要内容,如果未能解决你的问题,请参考以下文章