我试图将矢量化检查成一个简单的循环。我正在使用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
我不确定没有为无矢量化编译激活优化。如果是这种情况,如何禁用它?
感谢您的帮助
答案 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%。