由于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。