使用 SSE(整数数组的简单加法)时性能会变差

Posted

技术标签:

【中文标题】使用 SSE(整数数组的简单加法)时性能会变差【英文标题】:Performance worsens when using SSE (Simple addition of integer arrays) 【发布时间】:2014-06-27 07:42:16 【问题描述】:

我正在尝试使用 SSE 内在函数来添加两个 32 位有符号 int 数组。但与线性加法相比,我的性能非常差。

平台 - Intel Core i3 550、GCC 4.4.3、Ubuntu 10.04(有点旧,是的)

#define ITER 1000
typedef union sint4_u 
        __m128i v;
        sint32_t x[4];
 sint4;

功能:

void compute(sint32_t *a, sint32_t *b, sint32_t *c) 
        sint32_t len = 96000;
        sint32_t i, j;

        __m128i x __attribute__ ((aligned(16)));
        __m128i y __attribute__ ((aligned(16)));
        sint4 z;

        for(j = 0; j < ITER; j++) 
                for(i = 0; i < len; i += 4) 
                        x = _mm_set_epi32(a[i + 0], a[i + 1], a[i + 2], a[i + 3]);
                        y = _mm_set_epi32(b[i + 0], b[i + 1], b[i + 2], b[i + 3]);
                        z.v = _mm_add_epi32(x, y); 
                        c[i + 0] = z.x[3];
                        c[i + 1] = z.x[2];
                        c[i + 2] = z.x[1];
                        c[i + 3] = z.x[0];
                   
           

        return;


void compute_s(sint32_t *a, sint32_t *b, sint32_t *c) 
        sint32_t len = 96000;
        sint32_t i, j;
        for(j = 0; j < ITER; j++) 
                for(i = 0; i < len; i++) 
                        c[i] = a[i] + b[i];
                   
           
        return;

结果:

➜  C  gcc -msse4.2 simd.c
➜  C  ./a.out            
Time Elapsed (SSE): 612.520000 mS
Time Elapsed (Scalar): 401.713000 mS
➜  C  gcc -O3 -msse4.2 simd.c
➜  C  ./a.out                
Time Elapsed (SSE): 135.124000 mS
Time Elapsed (Scalar): 46.438000 mS

使用-O3 时,SSE 版本会慢 3 倍 (!!)。我究竟做错了什么?即使我在compute 中跳过加载回c,在没有任何优化的情况下仍然需要额外的100 毫秒。

编辑 - 按照 cmets 的建议,我将 _mm_set 替换为 _mm_load,这是更新时间 -

➜  C    gcc audproc.c -msse4    
➜  C    ./a.out             
Time Elapsed (SSE): 303.931000 mS
Time Elapsed (Scalar): 413.701000 mS
➜  C    gcc -O3 audproc.c -msse4
➜  C    ./a.out                 
Time Elapsed (SSE): 82.532000 mS
Time Elapsed (Scalar): 48.104000 mS

要好得多,但仍远未达到 4 倍的理论增益。另外,为什么O3 的矢量化速度较慢?另外,我该如何摆脱这个警告? (我尝试将__vector__ 添加到我的声明中,但收到了更多警告。:()

audproc.c: In function ‘compute’:
audproc.c:54: warning: passing argument 1 of ‘_mm_load_si128’ from incompatible pointer type /usr/lib/gcc/i486-linux-gnu/4.4.3/include/emmintrin.h:677: note: expected ‘const long long int __vector__ *’ but argument is of type ‘const sint32_t *’

【问题讨论】:

天哪……不会再来了……Please don't kill puppies. 哦,让我试着改变一下。 至少对于-O3,即使是 gcc 4.4 也应该比您的手动编码版本更好地矢量化循环。查看程序集(使用-S 标志,也许-fverbose-asm Integer dot product using SSE/AVX?的可能重复 【参考方案1】:

正如 cmets 中已经提到的,为了获得 SIMD 的性能优势,您应该避免在循环中进行标量操作,即摆脱 _mm_set_epi32 伪内在函数和用于存储 SIMD 结果的联合。这是您的函数的固定版本:

void compute(const sint32_t *a, const sint32_t *b, sint32_t *c)

    sint32_t len = 96000;
    sint32_t i, j;

    for(j = 0; j < ITER; j++)
    
        for(i = 0; i < len; i += 4)
        
            __m128i x = _mm_loadu_si128((__m128i *)&a[i]);
            __m128i y = _mm_loadu_si128((__m128i *)&b[i]);
            __m128i z = _mm_add_epi32(x, y); 
            _mm_storeu_si128((__m128i *)&c[i], z);
           
       

【讨论】:

虽然 96000 可以被 4 整除,但如果使用的值 len 不是 4 的倍数(例如 96001),这将有问题。 抱歉回复晚了 - 是的,这很好用。但是有一个问题,您使用_mm_loadu 来加载数据。我的数组已经是 16 字节对齐的,所以我切换到 _mm_load 以获得更好的性能,但在时间上没有发现差异。如果地址对齐,使用_mm_loadu 是否不会受到惩罚? @AnkushJain, since Nahelem the latency and throughput of _mm_load and _mm_loadu are the same 所以没有理由再使用_mm_load了。

以上是关于使用 SSE(整数数组的简单加法)时性能会变差的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode刷题989-简单-数组形式的整数加法

LeetCode刷题989-简单-数组形式的整数加法

SSE 整数与浮点数练习

在 C++ 中对整数数组进行线性搜索时,SSE 比较无法按预期工作

大数加法

大数的简单实现