sup*_*ggs 6 c gcc vectorization openmp
我一直在努力使我的代码能够被GCC自动矢量化,但是,当我包含该-fopenmp标志时,它似乎停止了所有自动矢量化的尝试.我正在使用它ftree-vectorize -ftree-vectorizer-verbose=5来矢量化并监控它.
如果我不包含该标志,它会开始向我提供有关每个循环的大量信息,如果它是矢量化的,为什么不.当我尝试使用该omp_get_wtime()函数时,编译器停止,因为它无法链接.一旦包含了标志,它就会简单地列出每个函数并告诉我它中的向量化0循环.
我读过这个问题已经提到了一些其他地方,但他们真的不来任何的解决方案:http://software.intel.com/en-us/forums/topic/295858 HTTP:// GCC. gnu.org/bugzilla/show_bug.cgi?id=46032.OpenMP有自己的处理矢量化的方法吗?我需要明确告诉它吗?
GCC矢量化器存在一个缺点,似乎已在最近的GCC版本中得到解决.在我的测试用例中,GCC 4.7.2成功地引导了以下简单循环:
#pragma omp parallel for schedule(static)
for (int i = 0; i < N; i++)
a[i] = b[i] + c[i] * d;
Run Code Online (Sandbox Code Playgroud)
同时GCC 4.6.1没有抱怨,循环包含无法分析的函数调用或数据引用.矢量化器中的错误parallel for由GCC实现循环的方式触发.处理和扩展OpenMP构造时,简单的循环代码转换为类似于此的东西:
struct omp_fn_0_s
{
int N;
double *a;
double *b;
double *c;
double d;
};
void omp_fn_0(struct omp_fn_0_s *data)
{
int start, end;
int nthreads = omp_get_num_threads();
int threadid = omp_get_thread_num();
// This is just to illustrate the case - GCC uses a bit different formulas
start = (data->N * threadid) / nthreads;
end = (data->N * (threadid+1)) / nthreads;
for (int i = start; i < end; i++)
data->a[i] = data->b[i] + data->c[i] * data->d;
}
...
struct omp_fn_0_s omp_data_o;
omp_data_o.N = N;
omp_data_o.a = a;
omp_data_o.b = b;
omp_data_o.c = c;
omp_data_o.d = d;
GOMP_parallel_start(omp_fn_0, &omp_data_o, 0);
omp_fn_0(&omp_data_o);
GOMP_parallel_end();
N = omp_data_o.N;
a = omp_data_o.a;
b = omp_data_o.b;
c = omp_data_o.c;
d = omp_data_o.d;
Run Code Online (Sandbox Code Playgroud)
在4.7之前的GCC中的矢量化器无法矢量化该循环.这不是特定于OpenMP的问题.人们可以很容易地重现它,完全没有OpenMP代码.为了证实这一点,我写了以下简单的测试:
struct fun_s
{
double *restrict a;
double *restrict b;
double *restrict c;
double d;
int n;
};
void fun1(double *restrict a,
double *restrict b,
double *restrict c,
double d,
int n)
{
int i;
for (i = 0; i < n; i++)
a[i] = b[i] + c[i] * d;
}
void fun2(struct fun_s *par)
{
int i;
for (i = 0; i < par->n; i++)
par->a[i] = par->b[i] + par->c[i] * par->d;
}
Run Code Online (Sandbox Code Playgroud)
可以预期两个代码(通知 - 这里没有OpenMP!)应该同样好地矢量化,因为restrict用于指定不会发生混叠的关键字.不幸的是,GCC <4.7不是这种情况 - 它成功地将循环fun1向量化,但是在fun2引用与编译OpenMP代码时相同的原因导致无法向量化.
这样做的原因是,vectoriser是无法证明par->d的存储器内不会说谎par->a,par->b和par->c点.情况并非总是这样fun1,有两种情况可能:
d 在寄存器中作为值参数传递;d 在堆栈上作为值参数传递.在x64系统上,System V ABI要求在XMM寄存器(启用AVX的CPU上的YMM)中传递前几个浮点参数.这就是d在这种情况下传递的方式,因此没有指针可以指向它 - 循环得到矢量化.在x86系统上,ABI要求将参数传递到堆栈,因此d可能由三个指针中的任何一个别名.实际上,fun1如果指示使用该-m32选项生成32位x86代码,GCC将拒绝向量化循环.
GCC 4.7通过插入运行时检查来解决这个问题,这些检查确保既d不会产生par->d别名也不会产生别名.
摆脱d删除无法修复的非别名,以下GMP 4.6.1对以下OpenMP代码进行矢量化:
#pragma omp parallel for schedule(static)
for (int i = 0; i < N; i++)
a[i] = b[i] + c[i];
Run Code Online (Sandbox Code Playgroud)