这个问题只是为了尝试更多地了解循环向量化,特别是使用OpenMP4。下面给出的代码生成' size'随机抽样,然后从这些样本中我们提取一块“q”。 ' qsize'来自某个位置的样本' qpos'程序然后找回' q'的位置。在'样本'阵列。这是代码:
#include <float.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <mm_malloc.h>
// SIMD size in floats, assuming 1 float = 4 bytes
#define VEC_SIZE 8
#define ALIGN (VEC_SIZE*sizeof(float))
int main(int argc, char *argv[])
{
if (argc!=4)
{
printf("Usage: %s <size> <qsize> <qpos>",argv[0]);
exit(1);
}
int size = atoi(argv[1]);
int qsize = atoi(argv[2]);
int qpos = atoi(argv[3]);
assert(qsize < size);
assert((qpos < size - qsize) && (qpos >= 0));
float *samples;
float *q;
samples = (float *) malloc(size*sizeof(float));
q = (float *) _mm_malloc(size*sizeof(float),ALIGN);
// Initialization
// - Randomly filling the samples
samples[0] = 0.0;
for (int i = 1 ; i < size; i++) //LOOP1
samples[i] = samples[i-1] + rand()/((float)RAND_MAX) - 0.5;
// - Getting q from the samples
#pragma omp simd aligned(q:ALIGN)
for (int i = 0; i < qsize; i++) //LOOP2
q[i] = samples[qpos+i];
// Finding the best match (since q is taken form the samples it self
// the position of the best match must be qpos)
float best_dist = FLT_MAX;
int pos = -1;
for (int i = 0; i < size - qsize; i++)//LOOP 3
{
float dist = 0;
#pragma omp simd aligned(q:ALIGN) reduction(+:dist)
for (int j = 0; j < qsize; j++)//LOOP4
dist += (q[j] - samples[i+j]) * (q[j] - samples[i+j]);
if (dist < best_dist)
{
best_dist = dist;
pos = i;
}
}
assert(pos==qpos);
printf("Done!\n");
free(samples);
_mm_free(q);
}
我使用以下命令使用icc 15.0.0和gcc 4.9.2编译它:
icc vec-test.c -o icc-vec-test -std=c11 -qopt-report=3 -qopt-report-phase=vec -qopt-report-file=icc.vec -O3 -xHost -fopenmp
gcc vec-test.c -o gcc-vec-test -std=c11 -fopt-info-vec-missed-optimized=gcc.vec -O3 -march=native -fopenmp
&#39; Q&#39;使用_mm_malloc()对齐。为样本做同样的事情是没有意义的。因为任何方式最内层循环(LOOP4)将始终访问它的未对齐元素。
gcc和icc都报告了LOOP4的矢量化(实际上,如果我们省略了#c; #pragma omp simd&#39;,gcc拒绝这样做,icc设法自动调整循环,但那只是一个额外观察)。从矢量化报告看来,编译器都没有生成剥离循环。我的问题:
1)编译器如何处理&#39;采样&#39;是不是已经签了名?
2)这会影响性能多少?
3)icc对LOOP2进行矢量化没有问题。但是gcc不能:&#34;注意:没有矢量化:基本块&#34;中没有足够的数据引用。有什么想法吗?
谢谢!
答案 0 :(得分:2)
以下是我测试运行流程包以测试可持续内存带宽时的一些经验。
1)据我所知,英特尔编译器不会生成用于检查对齐的代码,它会使用一些等效的movdqu
来加载样本,movdqa
用于加载q
2)这取决于内存带宽和可用触发器的比率。循环4只需要很少的计算量,我的猜测是你现在的HPC上的程序只是内存带宽限制,因为样本的大小和q很大,修复对齐没有多大帮助。但是,如果您将核心数量限制为<4,那么您应该能够观察到对齐样本的速度增益。
3)编译器没有根据对齐确定矢量化,当由于数据依赖性而导致矢量化不安全时,编译器将拒绝向量化。我对gcc的经验很少,所以我无法对此提出任何建议。
为了您的信息,在运行时检查对齐并提供使用对齐加载和寄存器间移位的专用例程通常可以胜过编译器生成的代码。您可以查看英特尔的L1 BLAS例程,了解他们如何执行此操作。