【发布时间】:2018-04-08 13:16:49
【问题描述】:
我正在尝试计算类似于 Fortran 中加权矩阵内积的东西。我用于计算内积的当前脚本如下
! --> In
real(kind=8), intent(in), dimension(ni, nj, nk, nVar) :: U1, U2
real(kind=8), intent(in), dimension(ni, nj, nk) :: intW
! --> Out
real(kind=8), intent(out) :: innerProd
! --> Local
integer :: ni, nj, nk, nVar, iVar
! --> Computing inner product
do iVar = 1, nVar
innerProd = innerProd + sum(U1(:,:,:,iVar)*U2(:,:,:,iVar)*intW)
enddo
但是我发现我目前使用的上述脚本效率不是很高。使用 NumPy 在 Python 中可以执行相同的操作,如下所示,
import numpy as np
import os
# --> Preventing numpy from multi-threading
os.environ['OPENBLAS_NUM_THREADS'] = '1'
os.environ['MKL_NUM_THREADS'] = '1'
innerProd = 0
# --> Toy matrices
U1 = np.random.random((ni,nj,nk,nVar))
U2 = np.random.random((ni,nj,nk,nVar))
intW = np.random.random((ni,nj,nk))
# --> Reshaping
U1 = np.reshape(np.ravel(U1), (ni*nj*nk, nVar))
U2 = np.reshape(np.ravel(U1), (ni*nj*nk, nVar))
intW = np.reshape(np.ravel(intW), (ni*nj*nk))
# --> Calculating inner product
for iVar in range(nVar):
innerProd = innerProd + np.dot(U1[:, iVar], U2[:, iVar]*intW)
使用 Numpy 的第二种方法似乎比使用 Fortran 的方法快得多。对于ni = nj = nk = nVar = 130的具体情况,两种方法所用时间如下
fortran_time = 25.8641 s
numpy_time = 6.8924 s
我尝试使用来自 BLAS 的ddot 改进我的 Fortran 代码,如下所示,
do iVar = 1, nVar
do k = 1, nk
do j = 1, nj
innerProd = innerProd + ddot(ni, U1(:,j,k,iVar), 1, U2(:,j,k,iVar)*intW(:,j,k), 1)
enddo
enddo
enddo
但时间并没有明显改善。对于ni = nj = nk = nVar = 130 的情况,上述方法所用的时间是~24s。 (我忘了提到我用'-O2'选项编译了Fortran代码以优化性能)。
不幸的是,Fortran 中没有用于逐元素矩阵乘法的 BLAS 函数。而且我不想在 Fortran 中使用 reshape,因为与 Python 不同,Fortran 中的 reshape 会导致将我的数组复制到一个新数组,从而导致更多的 RAM 使用。
有没有什么办法可以加快 Fortran 的性能,从而接近 Numpy 的性能?
【问题讨论】:
-
请注意 kind=8 是不可移植且丑陋的。
-
你确定 numpy 使用单核吗?
-
@AndrasDeak 是的,我很确定 numpy 只使用一个内核。我使用 htop 检查了核心使用情况。由于多线程,我非常有信心 NumPy 更快,但令我惊讶的是我错了。
-
我想知道将您的权重数组复制到完整大小并摆脱循环是否会有所帮助。你能试试吗? (我留下了这个评论,然后以为我很困惑并删除了它,但现在我再次认为这实际上与您的内在产品相对应;但我仍然不确定)。
-
我已经尝试过你的代码,循环在 0.6 秒内完成。您如何安排问题的时间?
标签: fortran matrix-multiplication