Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Performance 使子程序更快_Performance_Optimization_Fortran - Fatal编程技术网

Performance 使子程序更快

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

在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)
这一部分在代码中重复了好几次,所以我定义了这个子程序

!=============================================================================
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)
    几乎永远不会为真,除非您将相应的值设置为零。我假设您在调用子例程之前知道极点的顺序,那么为什么不传递它并保存此比较?如果不可能,请在主循环之前确定子例程中的极点数。然后,内部循环r
    k
    要简单得多
  • 在循环内部,您可以一次又一次地将输入矩阵转换为
    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
由于对原始问题进行了多次编辑,目前答案部分是多余的