3

我编译(使用 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 个),收集以秒为单位测量的挂墙时间(由程序打印出来)。(嗯,整个测试我跑了好几次,下面的时间绝对有代表性)

  1. 顺序执行。程序编译:

    • 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
  2. 多线程执行(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 时间总是相同的,那么程序也使用了多个线程。

我怎样才能从这些结果中获得一些意义?

4

0 回答 0