首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >折叠的`#pragma并行与`#pragma omp并行的差异

折叠的`#pragma并行与`#pragma omp并行的差异
EN

Stack Overflow用户
提问于 2021-03-10 14:23:42
回答 1查看 369关注 0票数 2

第一,这个问题可能有点误导,我明白平行地区的“崩溃”条款与没有“崩溃”条款的地区的主要不同之处。假设我想转换一个矩阵,下面有两个方法,第一个是用于内循环的并行SIMD指令,另一个是使用collapse(2)子句的第二个方法

代码语言:javascript
复制
#pragma omp parallel for
    for(int i=0; i<rows; i++){
#pragma omp simd
      for(int j=0; j<columns; j++){
         *(output + j * rows + i) = *(input + i * columns + j);
    }
}
代码语言:javascript
复制
#pragma omp parallel for collapse(2)
    for(int i=0; i<rows; i++){
      for(int j=0; j<columns; j++){
         *(output + j * rows + i) = *(input + i * columns + j);
    }

在上述两种方法中,哪种方法更有效,特别是在缓存方面?

在上述两种实现中,哪一种实现更高效、更快?我们是否可以通过查看实现来确定这一点。

并且,鉴于所有的循环计数器都是相互独立的,是否可以为何时使用时间设置一个基本的指导方针?

提亚

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-03-10 19:22:35

TL;DR:这两种实现效率都很低。在实践中,第二个可能会比第一个慢,尽管理论上它的规模可能更大。

第一个实现不太可能被向量化,因为访问在内存中并不是连续的。GCC 10和Clang 11都会生成效率低下的代码。关键是OpenMP没有提供高级SIMD结构来处理数据转换!因此,如果您想要高效地完成这个任务,您可能需要亲自动手(或者使用一个外部库来完成它)。

第二实现可能比第一实现慢得多,因为循环迭代器被线性化,通常导致在热路径上执行更多指令。实施(如)Clang 11和ICC 19,而不是GCC 10,甚至使用非常慢的模数运算(即。div指令),这样做会导致循环速度慢得多。

理论上,第二个实现应该比第一个更好地扩展,因为collapse子句提供了更多的并行性。实际上,在第一个实现中,只有rows行可以在n线程之间共享。因此,如果您在大规模并行机器或宽矩形矩阵上工作,nrows相比不太小,这可能会导致一些工作不平衡,甚至导致线程饥饿。

为什么两种实现都是低效的

由于内存访问模式,这两种实现效率很低。实际上,在大型矩阵上,output中的写操作并不是连续的,并且会导致许多缓存丢失。完整的缓存行(在大多数常见架构上为64字节)将被写入,而只有很少的字节将被写入其中。如果columns是两倍的幂,则会发生缓存重击并进一步降低性能。

缓解这些问题的一个解决方案是使用贴图。下面是一个示例:

代码语言:javascript
复制
// Assume rows and columns are nice for sake of clarity ;)
constexpr int tileSize = 8;
assert(rows % tileSize == 0);
assert(columns % tileSize == 0);

// Note the collapse clause is needed here for scalability and 
// the collapse overhead is mitigated by the inner loop.
#pragma omp parallel for collapse(2)
for(int i=0; i<rows; i+=tileSize)
{
    for(int j=0; j<columns; j+=tileSize)
    {
        for(int ti=i; ti<i+tileSize; ++ti)
        {
            for(int tj=j; tj<j+tileSize; ++tj)
            {
                output[tj * rows + ti] = input[ti * columns + tj];
            }
        }
    }
}

上面的代码应该更快,但不是最优的。成功地编写一个快速转换代码是很有挑战性的。下面是一些改进代码的建议:

instructions)

  • use

  • 使用临时块缓冲区来改进内存访问模式(因此编译器可以使用快速SIMD

  • 平方块来改进缓存

  • 的使用,使用多级平铺来改进L2/L3缓存的使用,或者使用Z平铺方法

)。

或者,您可以简单地使用一个快速的BLAS实现来提供相当优化的矩阵转换功能(不是所有的都是这样,但是AFAIK OpenBLAS和MKL确实是这样做的)。

PS:我假设矩阵是按行-主要顺序存储的。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66566669

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档