我编译(使用 GCC 和 PGI 编译器)并在两个不同的平台(基于 Haswell 和 Skylake)上运行了一个小型 Fortran/OpenMP 程序,只是为了感受一下性能的差异。我不知道如何解释结果——它们对我来说是个谜。
这是小程序(取自 Nvidia Developer 网站并稍作改编)。
PROGRAM main
use, intrinsic :: iso_fortran_env, only: sp=>real32, dp=>real64
use, intrinsic :: omp_lib
implicit none
real(dp), parameter :: tol = 1.0d-6
integer, parameter :: iter_max = 1000
real(dp), allocatable :: A(:,:), Anew(:,:)
real(dp) :: error
real(sp) :: cpu_t0, cpu_t1
integer :: it0, it1, sys_clock_rate, iter, i, j
integer :: N, M
character(len=8) :: arg
call get_command_argument(1, arg)
read(arg, *) N !!! N = 8192 provided from command line
call get_command_argument(2, arg)
read(arg, *) M !!! M = 8192 provided from command line
allocate( A(N,M), Anew(N,M) )
A(1,:) = 1
A(2:N,:) = 0
Anew(1,:) = 1
Anew(2:N,:) = 0
iter = 0
error = 1
call cpu_time(cpu_t0)
call system_clock(it0)
do while ( (error > tol) .and. (iter < iter_max) )
error = 0
!$omp parallel do reduction(max: error) private(i)
do j = 2, M-1
do i = 2, N-1
Anew(i,j) = (A(i+1,j)+A(i-1,j)+A(i,j-1)+A(i,j+1)) / 4
error = max(error, abs(Anew(i,j)-A(i,j)))
end do
end do
!$omp end parallel do
!$omp parallel do private(i)
do j = 2, M-1
do i = 2, N-1
A(i,j) = Anew(i,j)
end do
end do
!$omp end parallel do
iter = iter + 1
end do
call cpu_time(cpu_t1)
call system_clock(it1, sys_clock_rate)
write(*,'(a,f8.3,a)') "...cpu time :", cpu_t1-cpu_t0, " s"
write(*,'(a,f8.3,a)') "...wall time:", real(it1 it0)/real(sys_clock_rate), " s"
END PROGRAM
我使用的两个平台是:
- 英特尔 i7-4770 @ 3.40GHz (Haswell),32 GB RAM / Ubuntu 16.04.2 LTS
- Intel i7-6700 @ 3.40GHz (Skylake), 32 GB RAM / Linux Mint 18.1 (~ Ubuntu 16.04)
在每个平台上,我都编译了 Fortran 程序
- GCC gfortran 6.2.0
- PGI pgfortran 16.10 社区版
我显然在每个平台上独立编译了程序(我只移动了 .f90 文件;我没有移动任何二进制文件)
我运行了 4 个可执行文件中的每一个 5 次(每个平台 2 个),收集以秒为单位测量的挂墙时间(由程序打印出来)。(嗯,整个测试我跑了好几次,下面的时间绝对有代表性)
顺序执行。程序编译:
- gfortran -Ofast main.f90 -o gcc-seq
- pgfortran -fast main.f90 -o pgi-seq
时间安排(最好的 5 个):
- 哈斯韦尔 > gcc-seq:150.955,pgi-seq:165.973
- Skylake > gcc-seq:277.400,pgi-seq:121.794
多线程执行(8 个线程)。程序编译:
- gfortran -Ofast -fopenmp main.f90 -o gcc-omp
- pgfortran -fast -mp=allcores main.f90 -o pgi-omp
时间安排(最好的 5 个):
- 哈斯韦尔 > gcc-omp:153.819,pgi-omp:151.459
- Skylake > gcc-omp:113.497,pgi-omp:107.863
使用 OpenMP 编译时,我使用 omp_get_num_threads() 检查了并行区域中的线程数,实际上总是有 8 个线程,正如预期的那样。
有几件事我没有得到:
- 使用 GCC 编译器:为什么在 Skylake OpenMP 上具有显着优势(277 vs 113 s),而在 Haswell 上却完全没有优势?(150 vs 153 s) Haswell 发生了什么?
- 使用 PGI 编译器:为什么 OpenMP 在两个平台上都有这么小的好处(如果有的话)?
- 专注于顺序运行,为什么 Haswell 和 Skylake 之间的执行时间存在如此巨大的差异(尤其是当程序使用 GCC 编译时)?为什么这种差异仍然如此重要 - 但是 Haswell 和 Skylake 的角色颠倒了!- 何时启用 OpenMP?
- 此外,当启用 OpenMP 并使用 GCC 时,cpu 时间总是比 wall 时间大得多(正如我所料),但是当使用 PGI 时,cpu 和 wall 时间总是相同的,那么程序也使用了多个线程。
我怎样才能从这些结果中获得一些意义?