Parallel processing “#pragma parallel for collapse”与“#pragma omp parallel for collapse”之间的差异`

Parallel processing “#pragma parallel for collapse”与“#pragma omp parallel for collapse”之间的差异`,parallel-processing,openmp,vectorization,simd,Parallel Processing,Openmp,Vectorization,Simd,首先,这个问题可能有点误导,我理解平行区域和无平行区域的崩溃条款之间的主要区别。假设我想转置一个矩阵,有以下两种方法,第一种是用于内部循环的withSIMD指令的并行方法,第二种是使用collapse(2)子句的方法 #pragma omp parallel for for(int i=0; i<rows; i++){ #pragma omp simd for(int j=0; j<columns; j++){ *(output + j * ro

首先,这个问题可能有点误导,我理解平行区域和无平行区域的崩溃条款之间的主要区别。假设我想转置一个矩阵,有以下两种方法,第一种是用于内部循环的with
SIMD
指令的并行方法,第二种是使用
collapse(2)
子句的方法

#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);
    }
}
#pragma omp parallel for

对于(inti=0;iTL;DR:这两种实现都非常低效。在实践中,第二种实现可能比第一种慢,尽管理论上它可以更好地扩展

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

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

第二个实现在理论上也应该比第一个更好,因为
collapse
子句提供了更多的并行性。事实上,在第一个实现中,
n
线程之间只共享
行<代码>n
相比不是很小,这可能会导致一些工作不平衡,甚至线程不足


为什么两种实现都效率低下 由于内存访问模式,这两种实现效率很低。事实上,在大型矩阵上,
输出
中的写入不是连续的,并且会导致许多缓存未命中。完整的缓存线(大多数常见架构上为64字节)将被写入,而只向其中写入几个字节。如果
是二的幂,则会发生缓存抖动,并进一步降低性能

缓解这些问题的一个解决方案是使用平铺

//为了清晰起见,假设行和列都很好;)
constexpr int tileSize=8;
断言(行%tileSize==0);
断言(列%tileSize==0);
//注意,这里需要collapse子句以实现可伸缩性和可扩展性
//内部环路减轻了崩溃开销。
#用于折叠的pragma omp并行(2)

对于(int i=0;i与您的问题无关,但是对于编码风格,大多数人更喜欢
output[j*rows+i]
Lol是的,我实际上认为它是首先移动到内存位置,然后取消引用它。您的意思是
(output+j*rows)[i]
?如果这是您想要的语义,我会用C写它。或者如果您的意思是偏移到最终内存位置,然后是一个deref,
x[y]
是C语言中的语法糖,不多也不少。第二个语法糖的缺点是使用模块%操作在线程之间划分迭代,并且%在每个外部循环中调用一次,因此仍然是一个值得注意的问题overhead@dreamcrash好的观点!谢谢。但是请注意,这是没有具体说明的在OpenMP规范的指导下,理论上,编译器可以在最热的循环中生成没有模数的代码(即
div
指令)。实际上,这就是GCC所做的,尽管它仍然添加了一些额外的不需要的指令,使代码变慢。令人惊讶的是,Clang和ICC没有,而是在关键路径中使用了异常缓慢的
div
指令……我将编辑文章,以添加与这一点相关的更多信息。
#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);
    }