c - 如何将AVX2 vector 水平3乘3添加?

标签 c x86 simd intrinsics avx2

我有一个包含 16x16 位元素的 __m256i vector 。我想对其应用三个相邻的水平加法。在标量模式下,我使用以下代码:

unsigned short int temp[16];
__m256i sum_v;//has some values. 16 elements of 16-bit vector.   | 0 | x15 | x14 | x13 | ... | x3 | x2 | x1 |
_mm256_store_si256((__m256i *)&temp[0], sum_v);
output1 = (temp[0] + temp[1] + temp[2]);
output2 = (temp[3] + temp[4] + temp[5]);
output3 = (temp[6] + temp[7] + temp[8]);
output4 = (temp[9] + temp[10] + temp[11]);
output5 = (temp[12] + temp[13] + temp[14]); 
// Dont want the 15th element

因为这部分放在我程序的瓶颈部分,所以我决定使用 AVX2 进行矢量化。梦幻般的我可以像以下伪代码一样添加它们:
sum_v                                     //|  0  | x15 | x14 | x13 |...| x10 |...| x7 |...| x4 |...| x1 | 
sum_v1 = sum_v >> 1*16                    //|  0  |  0  | x15 | x14 |...| x11 |...| x8 |...| x5 |...| x2 |  
sum_v2 = sumv >> 2*16                     //|  0  |  0  |  0  | x15 |...| x12 |...| x9 |...| x6 |...| x3 |
result_vec = add_epi16 (sum_v,sum_v1,sum_v2)

//then I should extact the result_vec to outputs 

垂直添加它们将提供答案。
但不幸的是,AVX2 没有 256 位的移位操作,而 256 位寄存器被视为两个 128 位 channel 。我应该在这种情况下使用排列。但我找不到合适的 permutshuffle 等来执行此操作。对于这个实现有什么建议应该尽可能快。

使用 gcclinux mintintrinsicsskylake

最佳答案

您可以通过两个添加和只有 2 个“随机播放”来做到这一点:_mm256_bsrli_epi128 在零处移位
对答案不感兴趣的位置。对于 _mm256_permutevar8x32_epi32
我们选择一个重复高 32 位的排列,但这些位也是
与答案无关。

#include <stdio.h>
#include <x86intrin.h>
/*  gcc -O3 -Wall -m64 -march=haswell hor_sum3x3.c   */
int print_vec_short(__m256i x);
int print_12_9_6_3_0_short(__m256i x);

int main() {
   short x[16];

   for(int i=0; i<16; i++) x[i] = i+1; x[15] = 0;

   __m256i t0   = _mm256_loadu_si256((__m256i*)x);                              


   __m256i t1   = _mm256_bsrli_epi128(t0,2);             /* Shift 128 bit lanes in t0 right by 2 bytes while shifting in zeros. Fortunately the zeros are in the positions that we don't need */ 
   __m256i t2   = _mm256_permutevar8x32_epi32(t0,_mm256_set_epi32(7,7,6,5,4,3,2,1)); /* Shift right by 4 bytes     */
   __m256i sum  = _mm256_add_epi16(_mm256_add_epi16(t0,t1),t2);

   printf("t0  = ");print_vec_short(t0);
   printf("t1  = ");print_vec_short(t1);
   printf("t2  = ");print_vec_short(t2);
   printf("sum = ");print_vec_short(sum);

   printf("\nvector elements of interest: columns 12, 9, 6, 3, 0:\n");
   printf("t0[12, 9, 6, 3, 0]  = ");print_12_9_6_3_0_short(t0);
   printf("t1[12, 9, 6, 3, 0]  = ");print_12_9_6_3_0_short(t1);
   printf("t2[12, 9, 6, 3, 0]  = ");print_12_9_6_3_0_short(t2);
   printf("sum[12, 9, 6, 3, 0] = ");print_12_9_6_3_0_short(sum);
   return 0;
}


int print_vec_short(__m256i x){
   short int v[16];
   _mm256_storeu_si256((__m256i *)v,x);
   printf("%4hi %4hi %4hi %4hi | %4hi %4hi %4hi %4hi | %4hi %4hi %4hi %4hi  | %4hi %4hi %4hi %4hi \n",
          v[15],v[14],v[13],v[12],v[11],v[10],v[9],v[8],v[7],v[6],v[5],v[4],v[3],v[2],v[1],v[0]);
   return 0;
}

int print_12_9_6_3_0_short(__m256i x){
   short int v[16];
   _mm256_storeu_si256((__m256i *)v,x);
   printf("%4hi %4hi %4hi %4hi %4hi  \n",v[12],v[9],v[6],v[3],v[0]);
   return 0;
}

输出是:
$ ./a.out
t0  =    0   15   14   13 |   12   11   10    9 |    8    7    6    5  |    4    3    2    1 
t1  =    0    0   15   14 |   13   12   11   10 |    0    8    7    6  |    5    4    3    2 
t2  =    0   15    0   15 |   14   13   12   11 |   10    9    8    7  |    6    5    4    3 
sum =    0   30   29   42 |   39   36   33   30 |   18   24   21   18  |   15   12    9    6 

vector elements of interest: columns 12, 9, 6, 3, 0:
t0[12, 9, 6, 3, 0]  =   13   10    7    4    1  
t1[12, 9, 6, 3, 0]  =   14   11    8    5    2  
t2[12, 9, 6, 3, 0]  =   15   12    9    6    3  
sum[12, 9, 6, 3, 0] =   42   33   24   15    6  

关于c - 如何将AVX2 vector 水平3乘3添加?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42108358/

相关文章:

c - 了解 x86 IA32 程序集中函数调用的前/后汇编代码

c++ - simd 指令中不接受索引

c - make : *** [. o]错误1

c - 允许用户在文件中搜索单词的程序

gcc - GNU 汇编程序没有生成我可以执行的程序

assembly - x86 汇编语言中常量字符串的声明

c - 总线错误。无法访问内存

将字符串复制到指针似乎不起作用

x86 - SSE:_mm_load/store与使用直接指针访问之间的区别

c - GCC 中用于除法的 SIMD (SSE) 指令