我什么时候应该使用 DO CONCURRENT,什么时候应该使用 OpenMP?

phy*_*ets 6 concurrency fortran simd openmp fortran2008

我知道这个这个,但我再次询问,因为第一个链接现在已经很旧了,第二个链接似乎没有得出结论性的答案。达成共识了吗?

我的问题很简单:

我有一个循环,其中包含可以DO同时运行的元素。我该使用哪种方法?

下面是在简单立方晶格上生成粒子的代码。

  • npart是粒子数
  • npart_edgenpart_face分别是沿边和面的
  • space是晶格间距
  • RxRyRz是位置数组
  • x , y , z是决定晶格位置的临时变量

请注意,x、y 和 z 在 CONCURRENT 情况下必须是数组,但在 OpenMP 情况下则不然,因为它们可以定义为 PRIVATE。

我也使用DO CONCURRENT(据我从上面的链接了解,使用SIMD):

DO CONCURRENT (i = 1, npart)
    x(i) = MODULO(i-1, npart_edge)
    Rx(i) = space*x(i)
    y(i) = MODULO( ( (i-1) / npart_edge ), npart_edge)
    Ry(i) = space*y(i)
    z(i) = (i-1) / npart_face
    Rz(i) = space*z(i)
END DO
Run Code Online (Sandbox Code Playgroud)

或者我使用 OpenMP?

!$OMP PARALLEL DEFAULT(SHARED) PRIVATE(x,y,z)
!$OMP DO
DO i = 1, npart
    x = MODULO(i-1, npart_edge)
    Rx(i) = space*x
    y = MODULO( ( (i-1) / npart_edge ), npart_edge)
    Ry(i) = space*y
    z = (i-1) / npart_face
    Rz(i) = space*z
END DO
!$OMP END DO
!$OMP END PARALLEL
Run Code Online (Sandbox Code Playgroud)

我的测试:

将 64 个粒子放入 10 面的盒子中:

$ ifort -qopenmp -real-size 64 omp.f90
$ ./a.out 
CPU time =  6.870000000000001E-003
Real time =  3.600000000000000E-003

$ ifort -real-size 64 concurrent.f90 
$ ./a.out 
CPU time =  6.699999999999979E-005
Real time =  0.000000000000000E+000
Run Code Online (Sandbox Code Playgroud)

将 100000 个粒子放入面为 100 的盒子中:

$ ifort -qopenmp -real-size 64 omp.f90
$ ./a.out 
CPU time =  8.213300000000000E-002
Real time =  1.280000000000000E-002

$ ifort -real-size 64 concurrent.f90 
$ ./a.out 
CPU time =  2.385000000000000E-003
Real time =  2.400000000000000E-003
Run Code Online (Sandbox Code Playgroud)

使用该DO CONCURRENT构造似乎给我带来了至少一个数量级的更好性能。这是在 i7-4790K 上完成的。此外,并发性的优势似乎随着规模的增加而减弱。

Vla*_*r F 7

DO CONCURRENT 本身不进行任何并行化。编译器可能决定使用线程或使用 SIMD 指令进行并行化,甚至卸载到 GPU。对于线程,您通常必须指示它这样做。对于 GPU 卸载,您需要具有特定选项的特定编译器。或者(通常!),编译器只是将 DO CONCURENT 视为常规 DO,并在将它们用于常规 DO 时使用 SIMD。

OpenMP 也不仅仅是线程,编译器可以根据需要使用 SIMD 指令。还有omp simd指令,但这只是建议编译器使用SIMD,可以忽略。

你应该尝试、测量并观察。没有一个明确的答案。即使对于给定的编译器也不是这样,对于所有编译器来说更是如此。

如果您无论如何都不会使用 OpenMP,我会DO CONCURRENT尝试看看自动并行器是否可以更好地使用此构造。它很有可能会有所帮助。如果您的代码已经在 OpenMP 中,我认为引入DO CONCURRENT.

我的做法是使用 OpenMP 并尝试确保编译器能够向量化 (SIMD)。特别是因为我在我的程序中都使用了 OpenMP。DO CONCURRENT 仍然需要证明它确实有用。我还不相信,但一些 GPU 示例看起来很有希望 - 然而,真实的代码通常要复杂得多。


您的具体示例和绩效衡量:

给出的代码太少,并且每个基准测试都有微妙的地方。我围绕你的循环编写了一些简单的代码并做了我自己的测试。我很小心,没有将线程创建包含在定时块中。您不应该将其纳入$omp parallel您的时间安排中。我还在多次计算中采用了最短的实时时间,因为有时第一次的时间较长(当然是在 DO CONCURRENT 的情况下)。CPU 有多种节流模式,可能需要一些时间才能启动。我还补充道SCHEDULE(STATIC)

npart=10000000
ifort -O3 concurrent.f90: 6.117300000000000E-002
ifort -O3 concurrent.f90 -parallel: 5.044600000000000E-002
ifort -O3 concurrent_omp.f90: 2.419600000000000E-002

npart=10000,默认8个线程(超线程)
ifort -O3 concurrent.f90:5.430000000000000E-004
ifort -O3 concurrent.f90 -parallel:8.899999999999999E-005
ifort -O3 concurrent_omp.f90:1.890000000000000E-004

npart=10000, OMP_NUM_THREADS=4(忽略超线程)
ifort -O3 concurrent.f90: 5.410000000000000E-004
ifort -O3 concurrent.f90 -parallel: 9.200000000000000E-005
ifort -O3 concurrent_omp.f90: 1.070000000000000E-004

在这里,对于小型情况,DO CONCURRENT 似乎要快一些,但如果我们确保使用正确数量的内核,则速度不会太快。对于大案来说显然要慢一些。该-parallel选项对于自动并行化显然是必要的。