phy*_*ets 6 concurrency fortran simd openmp fortran2008
我知道这个和这个,但我再次询问,因为第一个链接现在已经很旧了,第二个链接似乎没有得出结论性的答案。达成共识了吗?
我的问题很简单:
我有一个循环,其中包含可以DO
同时运行的元素。我该使用哪种方法?
下面是在简单立方晶格上生成粒子的代码。
请注意,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 上完成的。此外,并发性的优势似乎随着规模的增加而减弱。
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
选项对于自动并行化显然是必要的。