在英特尔 Fortran 中编译单个源文件与多个源文件
Compiling single vs multiple source files in Intel Fortran
我一直在用不同文件中的模块和子例程编译一个项目。每个子程序都写在单独的文件中。模块也一样。然后,我测试了将这些文件分别编译为目标文件 (-c) 并与优化标志进行链接,还使用 cat 合并整个源代码并将相同的过程应用于此单个源文件。我发现编译单个文件生成的可执行文件比多个文件生成的可执行文件快大约 40%,尽管两者使用完全相同的标志。
我想知道是否有人知道为什么会这样,以及英特尔 Fortran 编译器上是否有任何标志将多个文件编译为单个文件。
应@chw21 的要求,我创建了一个小程序来显示问题:
program main
use operators
implicit none
integer :: n
real(8), dimension(:,:), allocatable :: a, b, c
integer :: i,j,k
n = 1000
allocate(a(n,n), b(n,n), c(n,n))
call random_number(a)
call random_number(b)
do j = 1, n
do i = 1, n
do k = 1, n
!c(i,j) = c(i,j) + a(k,i) * b(k,j)
c(i,j) = add(c(i,j), mul(a(k,i), b(k,j)))
enddo
enddo
enddo
write(*,*) sum(c)
end program
使用模块:
module operators
contains
function add(a,b) result (c)
real(8), intent(in) :: a, b
real(8) :: c
c = a + b
end function
function mul(a,b) result (c)
real(8), intent(in) :: a, b
real(8) :: c
c = a * b
end function
end module
我们的想法是,如果编译器知道它们非常小,那么这些函数通常应该被内联。我用 -O2
:
做了三个测试
- 单个文件中的完整源代码
- 分成两个文件
- 用
-ipo
(或-flto
)拆分成两个文件
ifort 13.0.0
和gfortran 5.2.0
在不同机器上的结果是:
Test | 1. | 2. | 3.
---------+-------+-------+-------
ifort | 1.3s | 15.7s | 1.9s
gfortran | 1.1s | 3.7s | 1.1s
不幸的是,我不知道为什么使用 ifort 进行的第一次和第三次测试之间仍然存在差异......我想,查看生成的代码可以对这个问题有所了解。
更新: 时间是通过执行 time ./a.out
测量的,这导致了稳定的时间。由于 ifort -O2
的标准编译,最大指令集应该是 SSE2(因此,没有 FMA),处理器最多支持 SSE4a(Opteron 6128)。对最近的英特尔处理器(最高为 AVX)的额外测试显示了类似的结果。
一件重要的事情似乎是缺少内循环的内联和矢量化,这在 IPO 和单文件编译期间得到应用(参见 --opt-report
)。此外,IPO 和单文件编译之间的向量化似乎存在一些差异。
我一直在用不同文件中的模块和子例程编译一个项目。每个子程序都写在单独的文件中。模块也一样。然后,我测试了将这些文件分别编译为目标文件 (-c) 并与优化标志进行链接,还使用 cat 合并整个源代码并将相同的过程应用于此单个源文件。我发现编译单个文件生成的可执行文件比多个文件生成的可执行文件快大约 40%,尽管两者使用完全相同的标志。 我想知道是否有人知道为什么会这样,以及英特尔 Fortran 编译器上是否有任何标志将多个文件编译为单个文件。
应@chw21 的要求,我创建了一个小程序来显示问题:
program main
use operators
implicit none
integer :: n
real(8), dimension(:,:), allocatable :: a, b, c
integer :: i,j,k
n = 1000
allocate(a(n,n), b(n,n), c(n,n))
call random_number(a)
call random_number(b)
do j = 1, n
do i = 1, n
do k = 1, n
!c(i,j) = c(i,j) + a(k,i) * b(k,j)
c(i,j) = add(c(i,j), mul(a(k,i), b(k,j)))
enddo
enddo
enddo
write(*,*) sum(c)
end program
使用模块:
module operators
contains
function add(a,b) result (c)
real(8), intent(in) :: a, b
real(8) :: c
c = a + b
end function
function mul(a,b) result (c)
real(8), intent(in) :: a, b
real(8) :: c
c = a * b
end function
end module
我们的想法是,如果编译器知道它们非常小,那么这些函数通常应该被内联。我用 -O2
:
- 单个文件中的完整源代码
- 分成两个文件
- 用
-ipo
(或-flto
)拆分成两个文件
ifort 13.0.0
和gfortran 5.2.0
在不同机器上的结果是:
Test | 1. | 2. | 3.
---------+-------+-------+-------
ifort | 1.3s | 15.7s | 1.9s
gfortran | 1.1s | 3.7s | 1.1s
不幸的是,我不知道为什么使用 ifort 进行的第一次和第三次测试之间仍然存在差异......我想,查看生成的代码可以对这个问题有所了解。
更新: 时间是通过执行 time ./a.out
测量的,这导致了稳定的时间。由于 ifort -O2
的标准编译,最大指令集应该是 SSE2(因此,没有 FMA),处理器最多支持 SSE4a(Opteron 6128)。对最近的英特尔处理器(最高为 AVX)的额外测试显示了类似的结果。
一件重要的事情似乎是缺少内循环的内联和矢量化,这在 IPO 和单文件编译期间得到应用(参见 --opt-report
)。此外,IPO 和单文件编译之间的向量化似乎存在一些差异。