运行时不可分割的循环大小对openMP SIMD的影响

时间:2019-02-25 11:53:24

标签: c openmp simd

在阅读了几篇不同的文章并没有找到答案之后,我将介绍问题,然后提出问题。

我有一段代码,可以简化为如下所示的一系列循环。

#pragma omp parallel for simd
for(int i = 0; i < a*b*c; i++)
{
    array1[i] += array2[i] * array3[i];
}

现在,我遇到的大多数SIMD使用示例都在编译时固定了a,b和c,以便进行优化。但是,我的代码要求a和b的值在运行时确定。

让我们说,对于计算机,我正在使用的寄存器可以容纳4个值,而a b c的值是127。我对此的理解是编译时间编译器将矢量化所有可被4整除的东西,然后将其余部分序列化(如果我错了,请更正此错误)。但是,这是编译器完全了解该问题的时候。如果现在允许我选择a,b和c的运行时并取值为127,那么矢量化将如何进行?天真的,我认为幕后的代码足够智能,可以理解这种情况可能同时发生,既有串行代码又有矢量代码,并且调用最合适。但是,因为这是一个假设,所以我希望有人对此主题有更多的了解,对我有进一步的启发,因为我不希望由于误解而意外溢出或不处理数据。

在这个偶然的机会上,我正在将OpenMP 4.0与C gcc编译器一起使用,尽管我希望这不会改变您的答案,因为我将始终尝试使用最新的OpenMP版本,并且不幸的是,可能需要定期更改编译器。

1 个答案:

答案 0 :(得分:0)

通常,编译器将展开超出simd长度的部分。为了获得最佳效果,尤其是在使用gcc时,您可以指定此展开系数,例如--param max-unroll-times = 2(如果您不希望循环更长)。 simd的长度为4,则循环一次将消耗8次迭代,剩下的则是剩余的。 gcc会建立一个余数循环,有点像Duff的设备,它可能有15次迭代,并会在运行时计算要跳转到的位置。英特尔编译器以不同的方式处理矢量化余数循环。假设您有2个simd宽度可用,其余的循环将使用较短的宽度而不会展开,从而使串行部分尽可能短。当针对未对齐数据的一般情况进行编译时,两端都有余数循环,开始时的余数限制为对齐存储值所需的长度。使用omp并行simd组合,情况变得更加复杂。通常,循环块的大小必须有所不同,有人可能会争辩说内部块可以设置为对齐,而末端块较小(通常不做)。