我正在尝试在 Fortran 中编写一个函数,它将多个具有不同权重的矩阵相乘,然后将它们加在一起形成一个矩阵。我已经确定这个过程是我程序中的瓶颈(这个权重将在程序的单次运行中进行多次,具有不同的权重)。现在我正试图通过从 Matlab 切换到 Fortran 来使其运行得更快。我是 Fortran 的新手,所以我感谢所有帮助。
在 Matlab 中,我发现进行此类计算的最快方法如下所示:
function B = weight_matrices()
n = 46;
m = 1800;
A = rand(n,m,m);
w = rand(n,1);
tic;
B = squeeze(sum(bsxfun(@times,w,A),1));
toc;
在我的机器(Matlab R2012b,MacBook Pro 13" Retina,2.5 GHz Intel Core i5,8 GB 1600 MHz DDR3)上分配的行在B
大约 0.9 秒内运行。应该注意的是,对于我的问题,张量A
将是整个程序运行(初始化后)相同(常量),但 w 可以取任何值。此外,这里使用n
和的典型值m
,这意味着张量A
在内存中的大小约为 1 GB。
我能想到的用 Fortran 写这个的最清晰的方法是这样的:
pure function weight_matrices(w,A) result(B)
implicit none
integer, parameter :: n = 46
integer, parameter :: m = 1800
double precision, dimension(num_sizes), intent(in) :: w
double precision, dimension(num_sizes,msize,msize), intent(in) :: A
double precision, dimension(msize,msize) :: B
integer :: i
B = 0
do i = 1,n
B = B + w(i)*A(i,:,:)
end do
end function weight_matrices
当使用 gfortran 4.7.2 编译时,使用 -O3(使用“call cpu_time(t)”计时的函数调用),此函数在大约 1.4 秒内运行。如果我手动将循环解包到
B = w(1)*A(1,:,:)+w(2)*A(2,:,:)+ ... + w(46)*A(46,:,:)
该函数需要大约 0.11 秒才能运行。这很棒,意味着与 Matlab 版本相比,我得到了大约 8 倍的加速。但是,我仍然对可读性和性能有一些疑问。
首先,我想知道是否有更快的方法来执行矩阵的加权和求和。我查看了 BLAS 和 LAPACK,但找不到任何似乎适合的功能。我还尝试将A
枚举矩阵的维度作为最后一个维度(即从元素切换(i,j,k)
到(k,i,j)
元素),但这会导致代码变慢。
其次,这个快速版本不是很灵活,而且实际上看起来很丑,因为对于这样一个简单的计算来说,它包含了太多的文本。对于我正在运行的测试,我想尝试使用不同数量的权重,以便 w 的长度会有所不同,以了解它如何影响我的算法的其余部分。但是,这意味着我B
每次重写作业都相当繁琐。有什么方法可以让这更灵活,同时保持性能相同(或更好)?
第三,A
如前所述,张量将在程序运行期间保持不变。我在我的程序中使用他们自己模块中的“参数”属性设置了常量标量值,并使用“使用”表达式将它们导入到需要它们的函数/子例程中。为张量做等效事情的最佳方法是什么A
?我想告诉编译器这个张量在 init. 之后将是常量,以便可以进行任何相应的优化。请注意,A
它的大小通常约为 1 GB,因此直接在源文件中输入它是不切实际的。
提前感谢您的任何意见!:)