在 Fortran 中计算组件张量变化的有效方法

Efficient way to compute tensor change of components in Fortran

这是 question 的跟进。

我有一个大小为 NxMxM 的数组 D(:,:,:)。通常,对于我现在正在考虑的问题,它是 M=400 和 N=600000(我重塑了数组,以便为​​第一个条目提供最大的大小)。

因此,对于第一项的每一个值l,D(l,:,:)是某个基上的MxM矩阵。我需要使用大小为 MxM 的基集 vec(:,:) 执行此矩阵的组件更改,以获得矩阵 D_mod(l,:,:).

我认为最简单的方法是:

         D_mod=0.0d0
          
         do b=1,M
         do a=1,M

         do   nu=1,M
         do   mu=1,M
             D_mod(:,mu,nu)=D_mod(:,mu,nu)+vec(mu,a)*vec(nu,b)*D(:,a,b)
         end do
         end do

         end do
         end do

有没有办法提高这个计算的速度(也使用 LAPACK/BLAS 库)?

我正在考虑这种方法:将 D 重塑为 N x M^2 矩阵 D_re;做张量积 vec(:,:) x vec(:,:) 并重塑它以获得 M^2 x M^2 矩阵 vecsq_re(:,:) (这激发了这个 question);最后,用 zgemm 做这两个矩阵的矩阵乘积。但是,我不确定这是一个好的策略。

编辑 对不起,我写的问题太快太晚了。大小可以达到 600000,是的,但我通常会采取至少减少 10 倍的策略。该代码应该 运行 在具有 100 Gb 内存的节点上

正如@IanBush 所说,您的 D 数组非常庞大,您可能需要某种 high-memory 机器或机器集群来同时处理它。但是,您可能不需要一次处理所有内容。

在开始之前,让我们先假设您没有任何内存问题。对于您描述的操作,D 看起来像一个 N 矩阵数组,每个矩阵的大小为 M*M。你说你已经“重塑数组以便为第一个条目提供最大的大小”,但是对于这个问题,这与你想要的完全相反。 Fortran 是一种 语言,因此遍历数组的第一个索引比遍历最后一个索引快得多。实际上,这意味着 triple-loop 例如

do i=1,N
  do j=1,M
    do k=1,M
      D(i,j,k) = D(i,j,k) +1
    enddo
  enddo
enddo

将 运行 比 re-ordered triple-loop

慢得多 1
do k=1,M
  do j=1,M
    do i=1,N
      D(i,j,k) = D(i,j,k) +1
    enddo
  enddo
enddo

因此您可以通过将 DD_modN*M*M 数组转换为 M*M*N 数组并重新排列循环来立即加快一切。您还可以通过将 manually-written 矩阵乘法替换为 matmul(或 BLAS/LAPACK)来加快一切速度,从而得到

do i=1,N
  D_mod(:,:,i) = matmul(matmul(vec , D(:,:,i)),transpose(vec))
enddo

现在您一次对一个矩阵进行矩阵乘法,您还可以找到解决内存使用问题的方法:不要将所有内容都加载到内存中并尝试一次完成所有操作,只需加载一个 [=一次将 13=] 矩阵放入 M*M 数组,计算 D_mod 的相应条目,并在加载下一个矩阵之前将其写入磁盘。


1 如果你的编译器不只是优化循环顺序。