使用gcc-4.9将矢量化检查成一个简单的例子

时间:2016-09-19 01:07:02

标签: c gcc vectorization

我试图将矢量化检查成一个简单的循环。我正在使用MacOS 19.5,我的代码编译为gcc-mp-4.9(从Macports安装)。为了获得更好的矢量化性能,我测量进入主循环的经过时间,并将其与无矢量化版本进行比较。

以下是这个简单的代码(我使用"NOVEC""VEC" -D flag编译):

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define SIZE 1000000000

#ifdef NOVEC    
void addition_tab_novec(double *a, double *b, double *c)
{
 int i;

 for (i=0; i<SIZE; i++)
    c[i] = a[i] + b[i];
}
#endif

#ifdef VEC    
void addition_tab_vec(double * restrict a, double * restrict b, double * restrict c) 
{
 int i;

 double *x = __builtin_assume_aligned(a, 16);
 double *y = __builtin_assume_aligned(b, 16);
 double *z = __builtin_assume_aligned(c, 16);

 for (i=0; i<SIZE; i++)
    z[i] = x[i] + y[i];
}
#endif

int main(int argc, char *argv[])
{
  // Array index
  int i;

  // Two input arrays
  double *tab_x;
  double *tab_y;
  double *tab_z;

  // Time elapsed
  time_t time1, time2;

  // Allocation
  tab_x = (double*) malloc(SIZE*sizeof(double));
  tab_y = (double*) malloc(SIZE*sizeof(double));
  tab_z = (double*) malloc(SIZE*sizeof(double));

  // Initialization
  for (i=0; i<SIZE; i++)
     {
      tab_x[i] = i;
      tab_y[i] = 2*i;
      tab_z[i] = 0.0;
     }

#ifdef NOVEC
     // Start time for vectorization
     time(&time1);

     // Addition function
     addition_tab_novec(tab_x, tab_y, tab_z);

     // Compute elapsed time for vectorization
     time(&time2);

     printf("No Vectorization - Time elapsed = %f seconds\n", difftime(time2, time1));
#endif

#ifdef VEC    
     // Start time for vectorization
     time(&time1);

     // Addition function
     addition_tab_vec(tab_x, tab_y, tab_z);

     // Compute elapsed time for vectorization
     time(&time2);

     printf("Vectorization - Time elapsed = %f seconds\n", difftime(time2, time1));
#endif

  return 0;
}

我的问题是,与无矢量化版本相比,我没有获得更好的矢量化结果。

鉴于我使用“__builtin_assume_aligned(array, 16)”,即16 bytes alignement,我希望在测量的循环中获得两倍的经过时间(我使用带有sizeof(double) = 8 bytes的双数组)

但实际上,我在没有矢量化的情况下获得了60秒,并且使用了59秒:我怎么能解释这些相同的结果呢?

以下是两种情况下的编译命令行:

无矢量化:

gcc-mp-4.9 -DNOVEC -std=c99 -fno-tree-vectorize main_benchmark.c

矢量化:

gcc-mp-4.9 -DVEC -std=c99 -Wa,-q -O3 -march=native -ftree-vectorize -fopt-info-vec main_benchmark.c

我不确定没有为无矢量化编译激活优化。如果是这种情况,如何禁用它?

感谢您的帮助

2 个答案:

答案 0 :(得分:0)

看到汇编程序代码会很有趣。

通常情况下,你最多可以期待两倍的性能,但是在这种情况下,考虑到阵列的大小,你不会在任何地方附近,CPU只是闲置,等待数据,是在它可以做任何事情之前进入并冲出缓存,这可能就是为什么没有收获。

处理大量数据时,也可能涉及内存管理开销,因此最好锁定内存,请参阅mlock联机帮助页。

答案 1 :(得分:-1)

首先,可以删除__builtin_assume_aligned变体,因为矢量化程序会自动对齐数据,并使用专门用于未对齐数据的特殊变体。但你明确对齐和限制改善了代码是正确的。

gcc-mp-6 -g -DVEC -std = c99 -Wa,-q -O3 -march = native -ftree-vectorize -fopt-info-vec -ftree-vectorizer-verbose = 2 vec-sample.c -o vec-vec2

vec-sample.c:12:2: note: loop vectorized
vec-sample.c:12:2: note: loop versioned for vectorization because of possible aliasing
vec-sample.c:12:2: note: loop peeled for vectorization to enhance alignment
vec-sample.c:50:3: note: loop vectorized
vec-sample.c:50:3: note: loop peeled for vectorization to enhance alignment
vec-sample.c:12:2: note: loop vectorized
vec-sample.c:12:2: note: loop peeled for vectorization to enhance alignment

$ otool -tv vec-vec2

...
0000000100000920    vmovupd (%r10,%rax), %ymm0
0000000100000926    vaddpd  (%r14,%rax), %ymm0, %ymm0
000000010000092c    addl    $0x1, %ecx
000000010000092f    vmovupd %ymm0, (%r8,%rax)
...

其次,循环中的加法代码是矢量化的(参见上面的vaddpd),但是setup和malloc在循环中占主导地位。没有多少东西可以测量循环。

为了测量循环(仅限osx),我使用了

#include <mach/mach_time.h>
#define SIZE 1000000
...
  // Time elapsed
  uint64_t t1, t2;
...
  // Start time for vectorization
  t1 = mach_absolute_time();

  // Addition function
  addition_tab_vec(tab_x, tab_y, tab_z);

  // Compute elapsed time for vectorization
  t2 = mach_absolute_time();

  printf("Vectorization - Time elapsed = %ld ticks\n", t2-t1);

使用SSE代码可以获得可测量的20%开销,而使用-O0则没有SSE可以获得62%。

http://locklessinc.com/articles/vectorize/更多地讨论了细节。