Performance 使子程序更快
在Fortran代码的主要部分,我有以下几行代码Performance 使子程序更快,performance,optimization,fortran,Performance,Optimization,Fortran,在Fortran代码的主要部分,我有以下几行代码 Gmat=0 do i=1,indCompMax do j=(i-1)*useSymFlag+1,nsit-(i-1)*useSymFlag l=1 do while (G0poles(l,2)/=0) Gmat(i,j)=Gmat(i,j)+real(G0int(i,j,l))/(omega(k)-G0poles(l,1))**G0poles(l,3) l=l+1 enddo enddo enddo call ExtendBySymmetry
Gmat=0
do i=1,indCompMax
do j=(i-1)*useSymFlag+1,nsit-(i-1)*useSymFlag
l=1
do while (G0poles(l,2)/=0)
Gmat(i,j)=Gmat(i,j)+real(G0int(i,j,l))/(omega(k)-G0poles(l,1))**G0poles(l,3)
l=l+1
enddo
enddo
enddo
call ExtendBySymmetry(Gmat)
这一部分在代码中重复了好几次,所以我定义了这个子程序
!=============================================================================
SUBROUTINE EvaluateFunc(matrixPol,matrixInt,z,matrix)
use NAGmodule
integer i,j,k
REAL*8, DIMENSION(Npoles,3) :: matrixPol
COMPLEX*16, DIMENSION(nsit,nsit,Npoles) :: matrixInt
COMPLEX*16, DIMENSION(nsit,nsit) :: matrix
COMPLEX*16 :: z
do i=1,indCompMax
do j=(i-1)*useSymFlag+1,nsit-(i-1)*useSymFlag
k=1
do while (matrixPol(k,2)/=0)
matrix(i,j)=matrix(i,j)+real(matrixInt(i,j,k))/(z-matrixPol(k,1))**matrixPol(k,3)
k=k+1
enddo
enddo
enddo
call ExtendBySymmetry(matrix)
end
问题是,如果我使用这个子程序,对输出矩阵的求值要比在代码的主要部分直接进行的相同求值花费更长的时间(大约慢5倍)。
如何优化代码并使使用子例程进行的计算更快
更新:谢谢你的回复。首先,操作
**matrixPol(k,3)
也出现在主代码中,我忘了在帖子中写它
对于比较(matrixPol(k,2)/=0)
没有问题,因为实际上从向量的某个位置开始,所有元素都正好为零
在i,j
循环之外计算预因子有助于加快子例程的速度。切换两个指数i和j实际上没有效果。下面是子程序的运行时间
!=============================================================================
SUBROUTINE EvaluateFunc(matrixPol,matrixInt,z,matrix)
use NAGmodule
integer i,j,k
REAL*8, DIMENSION(Npoles,3) :: matrixPol
COMPLEX*16, DIMENSION(nsit,nsit,Npoles) :: matrixInt
COMPLEX*16, DIMENSION(nsit,nsit) :: matrix
COMPLEX*16 :: z
do i=1,indCompMax
do j=(i-1)*useSymFlag+1,nsit-(i-1)*useSymFlag
k=1
do while (matrixPol(k,2)/=0)
matrix(i,j)=matrix(i,j)+real(matrixInt(i,j,k))/(z-matrixPol(k,1))**matrixPol(k,3)
k=k+1
enddo
enddo
enddo
call ExtendBySymmetry(matrix)
end
主要部分
1.688s
我的旧子程序
19.063s
系数在回路i,j之外
5.193s
切换索引i和j
5.281s
用dot_积
4.958s
但是子例程仍然慢了2.5倍以上
下面是一个简单的例子:
module NAGmodule
implicit none
real*8, allocatable :: hmat(:,:),eval(:),eigmat(:,:)
real*8, allocatable :: G0poles(:,:)
complex*16, allocatable :: G0int(:,:,:)
complex*16, allocatable :: Gmat(:,:)
real*8, allocatable :: omega(:)
integer :: numpoles, halffillingflag, iter, indCompMax
complex*16 :: omegaComplex
real*8, parameter :: pi=3.141592653589793
integer, parameter :: out_unit=10
integer, parameter :: timeFag=1
integer :: counti, countf, count_rate
real :: dt
integer, parameter :: Npoles=1000
real*8, parameter :: U=4
real*8, parameter :: omegamin=-20
real*8, parameter :: omegamax=20
integer, parameter :: Nomega=1500000
integer, parameter :: nsit = 4
integer, parameter :: nup = 1
integer, parameter :: ndw = 1
integer, parameter :: PBCflag=1
integer, parameter :: useSymFlag=1
end module NAGmodule
use nagmodule
integer :: i,j,k,l,m,n,p,q
REAL*8 t1,t2
allocate(hmat(nsit,nsit),eigmat(nsit,nsit),eval(nsit))
allocate(G0poles(Npoles,3),G0int(nsit,nsit,Npoles))
allocate(omega(Nomega))
allocate(Gmat(nsit,nsit))
indCompMax=1
hmat=0.
do i=1,(nsit-1)
hmat(i,i+1)=-1
hmat(i+1,i)=-1
enddo
if (PBCflag==1) then
hmat(1,nsit)=-1
hmat(nsit,1)=-1
end if
call NAGdiag(nsit)
eigmat=hmat
do k=1,Nomega
omega(k)=(omegamax-omegamin)/(Nomega-1)*(k-1)+omegamin
enddo
do k=1,nup
G0poles(k,1)=eval(k)
G0poles(k,2)=-1
G0poles(k,3)=1
enddo
do k=(nup+1),nsit
G0poles(k,1)=eval(k)
G0poles(k,2)=1
G0poles(k,3)=1
enddo
do k=1,nsit
G0int(k,k,k)=1
if ((k==nup).AND.(abs(eval(k)-eval(k+1))<1e-15)) then
G0int(k,k,k)=0.5
G0int(k+1,k+1,k)=0.5
else if ((k==nup+1).AND.(abs(eval(k)-eval(k-1))<1e-15)) then
G0int(k,k,k)=0.5
G0int(k-1,k-1,k)=0.5
end if
enddo
do k=1,nsit
G0int(:,:,k)=matmul(eigmat,matmul(G0int(:,:,k),transpose(eigmat)))
enddo
t1=0
t2=0
do k=1,Nomega
omegaComplex=CMPLX(omega(k),0)
call system_clock(counti,count_rate)
Gmat=0
call EvaluateFunc3(G0poles,G0int,omegaComplex,Gmat)
call system_clock(countf)
dt=REAL(countf-counti)/REAL(count_rate)
t1=t1+dt
call system_clock(counti,count_rate)
Gmat=0
do i=1,indCompMax
do j=(i-1)*useSymFlag+1,nsit-(i-1)*useSymFlag
l=1
do while (G0poles(l,2)/=0)
Gmat(i,j)=Gmat(i,j)+real(G0int(i,j,l))/(omega(k)-G0poles(l,1))
l=l+1
enddo
enddo
enddo
call ExtendBySymmetry(Gmat)
call system_clock(countf)
dt=REAL(countf-counti)/REAL(count_rate)
t2=t2+dt
enddo
write(*,*)'time with subroutine',t1
write(*,*)'time main',t2
stop
end
!=================================================================================
SUBROUTINE EvaluateFunc3(matrixPol,matrixInt,z,matrix)
use NAGmodule
integer i,j,k
REAL*8, DIMENSION(Npoles,3) :: matrixPol
COMPLEX*16, DIMENSION(nsit,nsit,Npoles) :: matrixInt
COMPLEX*16, DIMENSION(nsit,nsit) :: matrix
COMPLEX*16 :: z
integer :: maxPoles
COMPLEX*16, DIMENSION(Npoles) :: factor
maxPoles=0
do while (matrixPol(maxPoles+1,2)/=0)
maxPoles=maxPoles+1
enddo
factor(:maxPoles) = (1.,0.)/(z-matrixPol(:maxPoles,1))**matrixPol(:maxPoles,3)
do j=1,indCompMax
do i=(j-1)*useSymFlag+1,nsit-(j-1)*useSymFlag
matrix(i,j)=matrix(i,j)+dot_product(matrixInt(i,j,1:maxPoles),factor(1:maxPoles))
enddo
enddo
call ExtendBySymmetry2(matrix)
end
!=================================================================================
SUBROUTINE ExtendBySymmetry2(matrix)
use NAGmodule
COMPLEX*16, DIMENSION(nsit,nsit) :: matrix
integer k,i,j,l,m
if ((PBCflag==1).AND.(useSymFlag==1)) then
do i=2,nsit
matrix(2:nsit,i)=matrix(1:nsit-1,i-1)
matrix(1,i)=matrix(nsit,i-1)
enddo
else if ((PBCflag==0).AND.(useSymFlag==1)) then
do j=1,nsit/2
do i=j,nsit-j+1
matrix(j,i)=matrix(i,j)
matrix(nsit-i+1,nsit-j+1)=matrix(i,j)
matrix(nsit-j+1,nsit-i+1)=matrix(i,j)
enddo
enddo
end if
end
!=================================================================================
SUBROUTINE ExtendBySymmetry(matrix)
use NAGmodule
COMPLEX*16, DIMENSION(nsit,nsit) :: matrix
integer k,i,j,l,m
if ((PBCflag==1).AND.(useSymFlag==1)) then
do i=2,nsit
matrix(i,2:nsit)=matrix(i-1,1:nsit-1)
matrix(i,1)=matrix(i-1,nsit)
enddo
else if ((PBCflag==0).AND.(useSymFlag==1)) then
do i=1,nsit/2
do j=i,nsit-i+1
matrix(j,i)=matrix(i,j)
matrix(nsit-i+1,nsit-j+1)=matrix(i,j)
matrix(nsit-j+1,nsit-i+1)=matrix(i,j)
enddo
enddo
end if
end
!=================================================================================
SUBROUTINE NAGdiag(nsit1)
use NAGmodule
real*8, allocatable :: WORK(:)
integer, allocatable :: IWORK(:)
CHARACTER JOB, UPLO
EXTERNAL dsyevd
NMAX=nsit1
LDA=NMAX
LWORK=4*NMAX*NMAX+100
LIWORK=5*NMAX
LIWORK=10*NMAX
ALLOCATE(WORK(LWORK),IWORK(LIWORK))
JOB='V'
UPLO='L'
CALL dsyevd(JOB,UPLO,nsit1,hmat,LDA,eval,WORK,LWORK,IWORK,LIWORK,INFO)
IF (INFO.GT.0) THEN
WRITE (*,*) 'Failure to converge.'
stop
endif
deALLOCATE(WORK,IWORK)
return
end`
module模块
隐式无
实*8,可分配::hmat(:,:),eval(:),eigmat(:,:)
实*8,可分配::G0极点(:,:)
复杂*16,可分配::G0int(:,:,:)
复杂*16,可分配::Gmat(:,:)
实*8,可分配::ω(:)
整数::numpoles,半填充标志,iter,indCompMax
复合物*16::omegaComplex
实数*8,参数::pi=3.141592653589793
整数,参数::out\u单位=10
整数,参数::TimeFinder=1
整数::counti,countf,计数率
实数:dt
整数,参数::Npoles=1000
实数*8,参数::U=4
实数*8,参数::omegamin=-20
实数*8,参数::omegamax=20
整数,参数::Nomega=1500000
整数,参数::nsit=4
整数,参数::nup=1
整数,参数::ndw=1
整数,参数::PBCflag=1
整数,参数::useSymFlag=1
端模块
使用nagmodule
整数:i,j,k,l,m,n,p,q
实*8 t1,t2
分配(hmat(nsit,nsit),eigmat(nsit,nsit),评估(nsit))
分配(G0极(Npoles,3),G0int(nsit,nsit,Npoles))
分配(ω(Nomega))
分配(Gmat(nsit,nsit))
indCompMax=1
hmat=0。
Doi=1(nsit-1)
hmat(i,i+1)=-1
hmat(i+1,i)=-1
结束循环
如果(PBCflag==1),则
hmat(1,nsit)=-1
hmat(nsit,1)=-1
如果结束
呼叫NAGdiag(nsit)
eigmat=hmat
Dok=1,Nomega
omega(k)=(omegamax omegamin)/(Nomega-1)*(k-1)+omegamin
结束循环
do k=1,nup
G0极点(k,1)=评估(k)
G0极(k,2)=-1
G0极点(k,3)=1
结束循环
dok=(nup+1),nsit
G0极点(k,1)=评估(k)
G0极点(k,2)=1
G0极点(k,3)=1
结束循环
Dok=1,nsit
G0int(k,k,k)=1
如果((k==nup).AND.(abs(eval(k)-eval(k+1))尝试看看是否可以交换循环。因为Fortran按顺序存储数组
(1, 1), (2, 1), (3, 1), ..., (n, 1), (1, 2), (2, 2), ...
如果沿着该维度循环,内存访问速度要快得多。试着看看是否可以交换循环。因为Fortran按顺序存储数组
(1, 1), (2, 1), (3, 1), ..., (n, 1), (1, 2), (2, 2), ...
如果沿着该维度循环,内存访问速度会快得多。由于对原始问题进行了多次编辑,现在答案部分是多余的。但是,优化部分仍然有效
代码的真正问题是将z
作为复数传递给子例程(omegaComplex
),而omega(k)
是实数。这导致求幂和除法作为复杂操作而不是实数执行
将z
固定为实数(以及优化中的因子
)可以得到预期的结果。通过优化,我得到:
time with subroutine 0.24000001139938831
time main 0.35700001695659012
原始答复:
首先,子例程的操作与内联代码的操作不同。操作**matrixPol(k,3)
是复数的幂,需要大量计算。难怪子例程的速度要慢得多
我看到了一些加速代码的可能性:
- 除数
(z-matrixPol(k,1))**matrixPol(k,3)
独立于i
和j
,可以从循环中取出
- 除法比乘法更昂贵。因此,您应该在循环外预先计算
因子=1/div
,并在循环中与因子相乘
- 比较
(matrixPol(k,2)/=0)
几乎永远不会为真,除非您将相应的值设置为零。我假设您在调用子例程之前知道极点的顺序,那么为什么不传递它并保存此比较?如果不可能,请在主循环之前确定子例程中的极点数。然后,内部循环rk
要简单得多
- 在循环内部,您可以一次又一次地将输入矩阵转换为
real
。这可以在循环外部一次性完成。或者,更好的是,只将实数部分传递给函数
此时,您的代码看起来像:
!=============================================================================
SUBROUTINE EvaluateFunc(matrixPol,matrixInt,z,matrix)
use NAGmodule
integer i,j,k
REAL*8, DIMENSION(Npoles,3) :: matrixPol
COMPLEX*16, DIMENSION(nsit,nsit,Npoles) :: matrixInt
COMPLEX*16, DIMENSION(nsit,nsit) :: matrix
COMPLEX*16 :: z, factor(Npoles)
REAL*8, DIMENSION(nsit,nsit,Npoles) :: matrixInt_re
integer :: maxPoles
! Determine maximum number of poles
do k=1,Npoles
! Only valid if the poles are set to exactly zero outside. If not,
! use ( abs(matrixPol(k,2)) <= someEpsilon )
if ( matrixPol(k,2) == 0 ) then
maxPoles = k-1
exit
endif
enddo
! Pre-compute factors
factor(:maxPoles) = (1.,0.)/(z-matrixPol(:maxPoles,1))**matrixPol(:maxPoles,3)
! Convert input to real
matrixInt_re = real(matrixInt)
do i=1,indCompMax
do j=i,nsit-i+1
do k=1,maxPoles
matrix(i,j)=matrix(i,j)+matrixInt_re(i,j,k)*factor(k)
enddo
enddo
enddo
call ExtendBySymmetry(Gmat)
end
<> LI>如前所述,FORTRAN使用内存布局,并且以一种主要的方式访问它。这可能会丢失大量的内存。您应该考虑在主程序中转换数组,或者至少在“代码”> I/C>和<代码> J>代码>下切换循环。连续内存块上的med
由于对原始问题进行了多次编辑,目前答案部分是多余的