6

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

我的问题很简单:

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

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

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

请注意,在 CONCURRENT 情况下 x,y 和 z 必须是数组,但在 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

还是我使用 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

我的测试:

将 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

将 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

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

4

1 回答 1

6

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选项对于自动并行化显然是必要的。

于 2016-07-24T07:59:20.400 回答