【问题标题】:SSE instructions to add all elements of an array [duplicate]SSE指令添加数组的所有元素[重复]
【发布时间】:2012-06-11 10:27:22
【问题描述】:

我是 SSE2 指令的新手。我找到了一条指令_mm_add_epi8,它可以添加两个数组元素。但我想要一个可以添加数组所有元素的 SSE 指令。

我试图用这段代码来开发这个概念:

#include <iostream>
#include <conio.h>
#include <emmintrin.h>

void sse(unsigned char* a,unsigned char* b); 

void main()
{
    /*unsigned char *arr;
    arr=(unsigned char *)malloc(50);*/

    unsigned char arr[]={'a','b','c','d','e','f','i','j','k','l','m','n','o','p','q','r','a','b','c','d','e','f','i','j','k','l','m','n','o','p','q','r'};
    unsigned char *next_arr=arr+16;
    for(int i=0;i<16;i++)
          printf("%d,%c   ",next_arr[i],next_arr[i]);
    sse(arr,next_arr);

    getch();
}

void sse(unsigned char* a,unsigned char* b)                                                                                                                                                                          
{                                                                                                                                                                                                                                                                                                                                                                                            
  __m128i* l = (__m128i*)a;                                                                                                                                                                                      
  __m128i* r = (__m128i*)b; 
  __m128i result;

      result= _mm_add_epi8(*l, *r);

      unsigned char *p;
         p=(unsigned char *)&result;

        for(int i=0;i<16;i++)
          printf("%d ",p[i]);

         printf("\n");
         l=(__m128i*)p;
         r=(__m128i*)(p+8);         
         result=_mm_add_epi8(*l, *r);
         p=(unsigned char *)&result;
         printf("%d ",p[0]);

         l=(__m128i*)p;
         r=(__m128i*)(p+4);
         result=_mm_add_epi8(*l, *r);
         p=(unsigned char *)&result;
         l=(__m128i*)p;
         r=(__m128i*)(p+2);
         result=_mm_add_epi8(*l, *r);
         p=(unsigned char *)&result;
         l=(__m128i*)p;
         r=(__m128i*)(p+1);
         result=_mm_add_epi8(*l, *r);
          p=(unsigned char *)&result;
            printf("result =%d ",p[0]);
}

那么谁能告诉我如何使用 SSE2 指令添加数组的所有元素?

我们将不胜感激。

【问题讨论】:

  • 因重复而关闭,因为 psadbw 对于 8 位元素求和而不会溢出显着更有效,并且那里的答案使用了它。将它与 padddpaddq 一起用于大数组。

标签: c++ arrays sse simd sse2


【解决方案1】:

如果您只想对数组的所有元素求和,则需要加载数据,将其解压缩为更大的元素大小,然后对解压缩的元素求和。请注意,您可以保持多个部分总和,直到循环结束,然后只对这些部分总和进行最终总和。例如:

uint32_t sum_array(const uint8_t a[], int n)
{
    const __m128i vk0 = _mm_set1_epi8(0);       // constant vector of all 0s for use with _mm_unpacklo_epi8/_mm_unpackhi_epi8
    const __m128i vk1 = _mm_set1_epi16(1);      // constant vector of all 1s for use with _mm_madd_epi16
    __m128i vsum = _mm_set1_epi32(0);           // initialise vector of four partial 32 bit sums
    uint32_t sum;
    int i;

    for (i = 0; i < n; i += 16)
    {
        __m128i v = _mm_load_si128(&a[i]);      // load vector of 8 bit values
        __m128i vl = _mm_unpacklo_epi8(v, vk0); // unpack to two vectors of 16 bit values
        __m128i vh = _mm_unpackhi_epi8(v, vk0);
        vsum = _mm_add_epi32(vsum, _mm_madd_epi16(vl, vk1));
        vsum = _mm_add_epi32(vsum, _mm_madd_epi16(vh, vk1));
                                                // unpack and accumulate 16 bit values to
                                                // 32 bit partial sum vector

    }
    // horizontal add of four 32 bit partial sums and return result
    vsum = _mm_add_epi32(vsum, _mm_srli_si128(vsum, 8));
    vsum = _mm_add_epi32(vsum, _mm_srli_si128(vsum, 4));
    sum = _mm_cvtsi128_si32(vsum);
    return sum;
}

请注意,上述代码中有一个不明显的技巧——而不是将每个 16 位向量进一步解包为一对 32 位向量(需要 4 条解包指令),然后使用四个 32 位加法(另外 4 条指令) , 我们使用 _mm_madd_epi16 (PMADDWD) 和 1 的被乘数和 _mm_add_epi32 来有效地给我们免费解包,所以我们使用 4 条指令而不是 8 条得到相同的结果。

另请注意,输入数组 a[] 需要 16 字节对齐,n 应该是 16 的倍数。

【讨论】:

  • 感谢回复。您的代码在第 10、11、13、14 和 17 行显示错误。指令 _mm_madd_epi16 不能接受 3 个参数。 vk0 是未定义的?请解决这些错误。
  • 对不起——当你把一些工作代码编辑成一个简单的例子时会发生这种情况——我认为它现在或多或少已经修复了。
  • 非常感谢..它正在工作...:)
  • 仅供参考,我在 Intel Xeon W3550 3.07GHz 处理器上进行了测试,与幼稚循环相比,它显示了 37% 的加速:sum = 0; for (i=0; i
  • 但是,当在函数中用于计算绝对差异的零均值和(计算机视觉)时,唯一的变化是使用优化的代码与上面的朴素代码来计算一个向量,结果快了 10 倍。
猜你喜欢
  • 2014-07-14
  • 1970-01-01
  • 1970-01-01
  • 2020-12-06
  • 2017-01-31
  • 2019-02-22
  • 2015-08-05
  • 2023-03-24
  • 2015-05-19
相关资源
最近更新 更多