OpenMP的效率与优化级别 [英] Efficiency of OpenMP vs optimisation levels
问题描述
我是Open MP的新手,但我已经对此困惑了几天,在网上找不到任何答案。希望这里有人能给我解释一下这个奇怪的现象。
我想比较同一程序的顺序版本和并行版本之间的运行时。当我在GCC-10上用-O或更高级别编译它们时,并行版本的运行速度比顺序版本(~5倍)快得多(但不同级别之间的差异相当小)。
但是,当我使用-O0编译这两个程序时,情况并非如此。事实上,当使用-O0计算这两个版本时,顺序版本甚至更快一些。我试着了解一些只在O1及以上版本中启用的优化是否有实质性的效果,但没有运气。根据记录,使用-OS编译比使用-O0编译要好,但效率远远低于-O1及以上。
有没有人注意到类似的东西?对此有什么解释吗?
谢谢!
=
以下是指向c文件的链接:sequential code,parallel code
推荐答案
所有循环的核心如下:
var += something;
在顺序代码中,每个var
都是一个局部堆栈变量,使用-O0
行编译为:
; Compute something and place it in RAX
ADD QWORD PTR [RBP-vvv], RAX
此处vvv
是堆栈帧中var
的偏移量,以RBP
中存储的地址为根。
使用OpenMP时,会对源代码进行某些转换,相同的表达式变为:
*(omp_data->var) = *(omp_data->var) + something;
其中omp_data
是指向结构的指针,该结构保存指向并行区域中使用的共享变量的指针。这将编译为:
; Compute something and store it in RAX
MOV RDX, QWORD PTR [RBP-ooo] ; Fetch omp_data pointer
MOV RDX, QWORD PTR [RDX] ; Fetch *(omp_data->var)
ADD RDX, RAX
MOV RAX, QWORD PTR [RBP-ooo] ; Fetch omp_data pointer
MOV QWORD PTR [RAX], RDX ; Assign to *(omp_data->var)
这是并行代码速度较慢的第一个原因--递增var
的简单操作涉及更多的内存访问。
第二个,也是更有力的原因是虚假共享。您有8个共享累加器:xa
、xb
等。每个累加器都有8个字节长,并在内存中对齐,总共64个字节。考虑到大多数编译器将这些变量放在内存中的方式,它们很可能会在同一缓存行或两个缓存行中相邻结束(x86-64上的缓存行是64字节长,并且作为一个单元进行读写)。当一个线程写入其累加器时,例如,线程0更新xa
,这将使累加器恰好在同一高速缓存线中的所有其他线程的高速缓存无效,并且它们需要从较高级别高速缓存甚至主存储器重新读取值。这太糟糕了。这是如此糟糕,以至于它导致的速度比不得不通过双指针间接访问累加器要糟糕得多。
-O1
有什么变化?它引入了寄存器优化:
register r = *(omp_data->var);
for (a = ...) {
r += something;
}
*(omp_data->var) = r;
尽管var
是一个共享变量,但OpenMP允许在每个线程中临时使用不同的内存视图。这允许编译器执行寄存器优化,其中var
的值在循环期间不会更改。
解决方案是将所有xa
、xb
等设置为私有。
这篇关于OpenMP的效率与优化级别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!