元素矩阵乘法的并行化

Flo*_*ian 3 fortran openmp elementwise-operations

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

我有NxN-matrices AT和想要的elementwise它们相乘并将结果保存在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
Run Code Online (Sandbox Code Playgroud)

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

现在发生的奇怪事情是,无论线程数量(1到4之间),执行时间大致保持不变(+ - 10%),而CPU时间随着线程数量的增加而增加.这让我觉得所有的线程都做同样的工作(因为我在OpenMP上犯了一个错误)因此需要相同的时间.

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

可能是有不同版本OpenMPgfortran已安装.如果这可能是原因,那么如果你能告诉我如何找到它就会很棒.

Hri*_*iev 8

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

!$OMP PARALLEL WORKSHARE
A(:,:) = T(:,:) * A(:,:)
!$OMP END PARALLEL WORKSHARE
Run Code Online (Sandbox Code Playgroud)

请注意,虽然这是非常标准的OpenMP代码,但是一些编译器,尤其是英特尔Fortran编译器(ifort),WORKSHARE只需通过构造的方式实现SINGLE构造,因此无需任何并行加速.另一方面,gfortran将此代码片段转换为隐式PARALLEL DO循环.请注意,除非明确写入,否则不会在工作共享构造内部gfortran并行化标准数组表示法.A = T * AA(:,:) = 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个内核的多套接字系统上运行时,程序可以访问更多的最后一级缓存和更高的主内存带宽(假设每个CPU插槽中有一个单独的内存控制器的NUMA系统),因此性能提高,但只是因为有更多的套接字,而不是因为有更多的核心.