为什么打开 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 毫秒。我已经尝试为 inVectmatrix 使用各种不同的大小,但在所有情况下,-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

首先,考虑到我可能是错的。我也是第一次看到这个问题,和你一样惊讶。

我刚刚研究了这个问题,我的理解如下。优化 -O0O3Ofast 和... 是为大多数一般(频繁)情况编写的。然而,在某些情况下(当 -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

希望对您有所帮助。