为什么打开 gfortran 编译器优化后 matmul 变慢?
Why is matmul slower with gfortran compiler optimization turned on?
如果我在我的 Mac 上使用 gfortran (Homebrew GCC 8.2.0)
来编译下面没有优化的简单程序 (-O0
),对 matmul
的调用始终在 ~90 毫秒内执行。如果我使用任何优化(标志 -O1
、-O2
或 -O3
),执行时间会增加到 ~250 毫秒。我已经尝试为 inVect
和 matrix
使用各种不同的大小,但在所有情况下,-O0
选项至少优于其他三个优化标志 2.5 的系数。如果我使用只有几百个元素的较小矩阵,但循环多次调用 matmul,性能损失会更糟,接近 10 倍。
有什么方法可以避免这种行为?我需要在我的代码的某些部分使用优化,但与此同时,我也想尽可能高效地执行矩阵乘法。
我使用命令 gfortran -ON sandbox.f90
编译包含以下代码的文件 sandbox.f90
,其中 N
是优化级别 0-3(未使用其他编译器标志)。打印 outVect
的第一个值只是为了防止 gfortran
优化变得聪明并完全跳过对 matmul
的调用。
我是 Fortran 新手,所以如果我在这里遗漏了一些明显的东西,我提前道歉。
program main
implicit none
real :: inVect(20000), matrix(20000,10000), outVect(10000)
real :: start, finish
call random_number(inVect)
call random_number(matrix)
call cpu_time(start)
outVect = matmul(inVect, matrix)
call cpu_time(finish)
print '("Time = ",f10.7," seconds. – First Value = ",f10.4)',finish-start,outVect(1)
end program main
首先,考虑到我可能是错的。我也是第一次看到这个问题,和你一样惊讶。
我刚刚研究了这个问题,我的理解如下。优化 -O0
、O3
、Ofast
和... 是为大多数一般(频繁)情况编写的。然而,在某些情况下(当 -O3
比 -O*<-O3
效率低时)优化会带来缺点。这是因为这些优化会隐式调用标志,从而缩短特定任务的执行时间。对于您的情况, -O3
强制要求所有 matmul()
函数都将被内联。这样的事情通常是好的,但对于大数组或多次调用此函数不是必需的。不知何故,内联 matmul()
的成本比内联函数获得的收益更重要(至少我是这样看的)。
为了避免这种行为,我建议使用标志 -O3 -finline-matmul-limit=0
来取消 matmul
函数的内联。使用标志 -O3 -finline-matmul-limit=0
导致的执行时间不比 -O0
.
获得的执行时间差
您可以使用 -finline-matmul-limit=n
,只有当所涉及的数组小于 n
时,您才会内联 matmul
函数。为了简单起见,我使用 n=0
。
希望对您有所帮助。
如果我在我的 Mac 上使用 gfortran (Homebrew GCC 8.2.0)
来编译下面没有优化的简单程序 (-O0
),对 matmul
的调用始终在 ~90 毫秒内执行。如果我使用任何优化(标志 -O1
、-O2
或 -O3
),执行时间会增加到 ~250 毫秒。我已经尝试为 inVect
和 matrix
使用各种不同的大小,但在所有情况下,-O0
选项至少优于其他三个优化标志 2.5 的系数。如果我使用只有几百个元素的较小矩阵,但循环多次调用 matmul,性能损失会更糟,接近 10 倍。
有什么方法可以避免这种行为?我需要在我的代码的某些部分使用优化,但与此同时,我也想尽可能高效地执行矩阵乘法。
我使用命令 gfortran -ON sandbox.f90
编译包含以下代码的文件 sandbox.f90
,其中 N
是优化级别 0-3(未使用其他编译器标志)。打印 outVect
的第一个值只是为了防止 gfortran
优化变得聪明并完全跳过对 matmul
的调用。
我是 Fortran 新手,所以如果我在这里遗漏了一些明显的东西,我提前道歉。
program main
implicit none
real :: inVect(20000), matrix(20000,10000), outVect(10000)
real :: start, finish
call random_number(inVect)
call random_number(matrix)
call cpu_time(start)
outVect = matmul(inVect, matrix)
call cpu_time(finish)
print '("Time = ",f10.7," seconds. – First Value = ",f10.4)',finish-start,outVect(1)
end program main
首先,考虑到我可能是错的。我也是第一次看到这个问题,和你一样惊讶。
我刚刚研究了这个问题,我的理解如下。优化 -O0
、O3
、Ofast
和... 是为大多数一般(频繁)情况编写的。然而,在某些情况下(当 -O3
比 -O*<-O3
效率低时)优化会带来缺点。这是因为这些优化会隐式调用标志,从而缩短特定任务的执行时间。对于您的情况, -O3
强制要求所有 matmul()
函数都将被内联。这样的事情通常是好的,但对于大数组或多次调用此函数不是必需的。不知何故,内联 matmul()
的成本比内联函数获得的收益更重要(至少我是这样看的)。
为了避免这种行为,我建议使用标志 -O3 -finline-matmul-limit=0
来取消 matmul
函数的内联。使用标志 -O3 -finline-matmul-limit=0
导致的执行时间不比 -O0
.
您可以使用 -finline-matmul-limit=n
,只有当所涉及的数组小于 n
时,您才会内联 matmul
函数。为了简单起见,我使用 n=0
。
希望对您有所帮助。