我目前正在优化部分代码,因此执行一些基准测试。

我有NxN个矩阵AT,想将它们按元素相乘并将结果再次保存在A中,即A = A*T。由于此代码不可并行化,因此将分配扩展为

!$OMP PARALLEL DO
do j = 1, N
    do i = 1, N
        A(i,j) = T(i,j) * A(i,j)
    end do
end do
!$OMP END PARALLEL DO


(完整的最小工作示例位于http://pastebin.com/RGpwp2KZ。)

现在发生的一件奇怪的事情是,无论线程数量(介于1和4之间)如何,执行时间都大致相同(+/- 10%),但是CPU时间随着线程数量的增加而增加。这使我认为所有线程都执行相同的工作(因为我在OpenMP上犯了一个错误),因此需要相同的时间。

但是在另一台计算机上(具有96个CPU内核可用),该程序的行为符合预期:随着线程数的增加,执行时间会减少。令人惊讶的是,CPU时间也减少了(最多约10个线程,然后又增加了)。

可能安装了不同版本的OpenMPgfortran。如果这可能是原因,那么如果您能告诉我如何找到答案,那就太好了。

最佳答案

从理论上讲,您可以使用特定于Fortran的OpenMP WORKSHARE指令来并行执行Fortran数组操作:

!$OMP PARALLEL WORKSHARE
A(:,:) = T(:,:) * A(:,:)
!$OMP END PARALLEL WORKSHARE


请注意,尽管这是相当标准的OpenMP代码,但是某些编译器(尤其是Intel Fortran编译器(ifort))仅通过WORKSHARE构造方法来实现SINGLE构造,因此没有并行提高速度。任何。另一方面,gfortran将此代码片段转换为隐式的PARALLEL DO循环。请注意,除非将gfortran显式编写为A = T * A,否则不会在工作共享构造内并行化标准数组符号A(:,:) = T(:,:) * A(:,:)

现在关于性能和缺乏加速的问题。 AT矩阵的每一列都占用(2 * 8) * 729 = 11664个字节。一个矩阵占用8.1 MiB,而两个矩阵合起来占用16.2 MiB。这可能超出了CPU的上一级缓存大小。同样,乘法代码的计算强度也很低-每次迭代获取32字节的内存数据,并在6个FLOP中执行一个复杂的乘法(4个实数乘法,1个加法和1个减法),然后将16个字节存储回内存,结果是(6 FLOP)/(48 bytes) = 1/8 FLOP/byte。如果认为该存储器是全双工的,即它在读取时支持写入,则强度会提高到(6 FLOP)/(32 bytes) = 3/16 FLOP/byte。随之而来的问题是内存受限,甚至单个CPU内核也可能能够饱和所有可用的内存带宽。例如,典型的x86内核每个周期可以淘汰两个浮点运算,如果以2 GHz运行,则可以提供4 GFLOP / s的标量数学运算。为了让此类内核忙于运行乘法循环,主存储器应提供(4 GFLOP/s) * (16/3 byte/FLOP) = 21.3 GiB/s。此数量或多或少超过了当前x86 CPU的实际内存带宽。而且这仅适用于具有非矢量化代码的单个内核。添加更多的内核和线程不会提高性能,因为内存根本无法足够快地传送数据以保持内核繁忙。相反,由于拥有更多线程会增加开销,因此性能会受到影响。当在具有96个内核的多插槽系统上运行时,该程序可以访问更多的最后一级缓存和更高的主内存带宽(假设NUMA系统在每个CPU插槽中都有单独的内存控制器),因此性能会提高,但这仅仅是因为有更多的套接字,而不是因为有更多的内核。

10-08 08:11