2016-03-19 5 views
0

をサブルーチンが速くてください、私はこの部分は、コード内で数回繰り返される私の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)がメインコードにもありますが、投稿に忘れてしまいました。

比較のためには、実際にはベクトルの特定の位置から開始しているため、すべての要素が正確にゼロであるため問題はありません。

ループの外側にあるプリファクタを計算すると、サブルーチンの速度が向上しました。 2つのインデックスiとjを切り替えることは、実質的に効果がありません。ここでは、サブルーチン

すべての実行中の時間は、私がj 5.193s

スイッチング、ループの外factorと 1.688s

私の古いサブルーチン 19.063s

主要部分でありますインデックスiおよびj 5.281s

dot_product 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` 
+1

最初のスニペットとサブルーチンの計算コードは異なっているようです。 –

+0

サブルーチンで 'ExtendBySymmetry(matrix)'を呼び出すべきではありませんか? –

+0

はい、申し訳ありません別のタイプミス – user3368447

答えて

3

元の質問のいくつかの編集のために、答えは今は部分的に余分です。ただし、最適化部分は有効です。

コードの実際の問題は、omega(k)が実数で、複素数としてzをサブルーチン(omegaComplex)に渡していることです。これは、累乗と除算が実際のものの代わりに複雑な演算として実行される結果になります。

zを実際に(そして最適化ではfactor)固定すると、期待される結果が得られます。

time with subroutine 0.24000001139938831  
time main 0.35700001695659012 

オリジナルの答え:私が得るの最適化とすべての

まず、サブルーチンは、あなたのインラインコードが行うのと同じ操作を行いません。操作**matrixPol(k,3)は、複雑な数値への威力であり、計算に多大な労力を必要とします。サブルーチンが非常に遅いのは不思議ではありません。

私はあなたのコードを加速するためのいくつかの可能性を参照してください。

除数 (z-matrixPol(k,1))**matrixPol(k,3)ijとは独立しており、ループから取り出すことができる
  • 分割は乗算よりも高価です。したがって、ループ外でfactor = 1/divをあらかじめ計算し、ループ内でfactorを乗算する必要があります。
  • 対応する値を正確にゼロに設定しない限り、比較(matrixPol(k,2)/=0)はほとんど真実にはなりません。私はサブルーチンを呼び出す前に極の順番を知っていると仮定しているので、それを渡して自分自身でこの比較を保存してみましょう。それが不可能な場合は、メインループの前にサブルーチン内の極数を決定します。それで、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 
    

    さらなる最適化:それはk以上内側のループはより多くの何もないことが明らかになり、このようなコードを書き換え

    この時点で、あなたのコードは、何かのように見えますドットプロダクト。これは、コンパイラによって高速化される可能性があります。メインループは、次のようになります。chw21として

do i=1,indCompMax 
    do j=i,nsit-i+1 
     matrix(i,j)=matrix(i,j) + & 
     dot_product(matrixInt_re(i,j,:maxPoles), factor(:maxPoles)) 
    enddo 
    enddo 
  • をFortranがcolumn majorメモリレイアウトを使用して、注意して、あなたは、行の主要な方法でそれをアクセスしています。これにより、潜在的に多くのメモリが失われます。メインプログラムに配列を入れ替えるか、少なくともループをijに切り替えることを検討してください。最初のオプションは、内側のドットプロダクトが連続したメモリチャンクで実行されるようにすることをお勧めします。
+0

返事をありがとう。最初に、 '** matrixPol(k、3)'という操作がメインコードにもありますが、投稿に忘れてしまいました。 – user3368447

+0

@ user3368447メインコードで指数関数を追加するだけでなく、サブルーチンの内部関数の反復回数も変更しました。私はこれもタイミングを変えると思う。あなたは比較を繰り返すことができますか? –

+0

はい申し訳ありませんが、異なる繰り返しは、投稿のタイプミスでした。タイミングはiとjで同じ反復で得られる – user3368447

2

は、あなたの周りにループを交換できるかどうかを確認してください。 Fortranは順序で配列を格納するため、

(1, 1), (2, 1), (3, 1), ..., (n, 1), (1, 2), (2, 2), ... 

その次元に沿ってループすると、メモリアクセスははるかに高速です。

関連する問題