派生类型访问时间与阵列访问时间

时间:2016-02-25 18:51:48

标签: arrays fortran derived-types allocatable-array

我有一个与多维数组或派生类型的访问时间相关的问题。我写了一个很好的算法。但是,此算法的主要部分使用%通过不同类型引用某些数据。例如,这是我的代码中最昂贵的循环:

   do c_elem = 1 , no_elems
        call tmp_element%init_element(no_fl)
        tmp_element%Gij(no_fl,1) = e_Gxa(c_elem) 
        tmp_element%Gij(no_fl,2) = e_Gax(c_elem) 
        GAX = tmp_element%Gij(no_fl,1)/GAA
        GXA = tmp_element%Gij(no_fl,2)/GAA
        do k = 1 , no_fl-1
            s1 = this%flNew%Iij(k)
            i1 = this%nodes(s1)%fl_id
            tmp_element%Gij(k,1) = this%elOld(c_elem)%Gij(i1,1) + GAX*this%flNew%Gij(no_fl,k)
            tmp_element%Gij(k,2) = this%elOld(c_elem)%Gij(i1,2) + this%flNew%Gij(k,no_fl)*GXA
        enddo
        call this%elOld(c_elem)%init_element(no_fl)
        this%elOld(c_elem)%Gij = tmp_element%Gij
   enddo

正如您所看到的那样,我通过%访问一些变量,并且大多数数组都有“可分配”变量,复数或整数。我读过有时使用派生类型可能会导致计算速度变慢,所以我决定重写我的算法直接在没有类型的数组上工作。现在只使用数组的相同循环看起来像:

    do c_elem = 1 , no_elems
        nGij1(no_fl,c_elem) = e_Gxa(c_elem)
        nGij2(no_fl,c_elem) = e_Gax(c_elem)
        GAX = e_Gxa(c_elem)/GAA
        GXA = e_Gax(c_elem)/GAA
        do k = 1 , no_fl-1
            i1 = fl_id(k)
            nGij1(k,c_elem) = oGij1(i1,c_elem) + GAX*fl_new_Gij(no_fl,k)
            nGij2(k,c_elem) = oGij2(i1,c_elem) + fl_new_Gij(k,no_fl)*GXA
        enddo
    enddo

我预计上面的代码比我使用派生类型的代码运行得更快。所以我检查了时间。以下是此部分代码的CPU时间:

  1. 0.1s用于派生类型方法(第一个代码)
  2. 3.2s用于数组方法(第二代码)(慢30倍)
  3. 我在编译和ifort英特尔编译器中只使用-O2选项。在上面的代码中,no_elems>> no_fl和大多数数组和类型都很复杂。很明显,两种算法的唯一区别是存储器访问时间。

    为什么两种情况的执行时间都有很大差异?

    编辑#1

    我试图做一个简短的例子,但是我无法获得与完整代码类似的结果......两种情况的时间几乎相同 - 数组稍快一些。

    我可以向你展示对我来说完全陌生的事情:我有一个类型:

    type element
      complex*16,allocatable :: Gij(:,:) 
      integer    :: no_sites    
      complex*16 :: Gp,Gxa,Gax  
      integer    :: i,j
      contains
      procedure,pass(this) :: free_element
      procedure,pass(this) :: init_element
      procedure,pass(this) :: copy_element
    end type element
    

    然后,在一个大循环的某个点上,我计算这样的值:

      ! derived type way
      this%elOld(c_elem)%Gax = GAX
      this%elOld(c_elem)%Gxa = GXA
      this%elOld(c_elem)%Gp  = this%elOld(c_elem)%Gp + GXA*GAX/GAA
      ! array way to do it
      e_Gax(c_elem) = GAX
      e_Gxa(c_elem) = GXA
      e_Gp (c_elem) = e_Gp (c_elem) + GXA*GAX/GAA
    

    其中:

      complex(16),allocatable :: e_Gax(:),e_Gxa(:),e_Gp(:)
    

    和elOld是另一种类型

    定义的元素数组
      type(element),allocatable :: elOld(:),elNew(:)
    

    然后我有循环,我已经告诉你了,但如果我这样做的话:

      tmp_element%Gij(no_fl,1) = e_Gxa(c_elem) 
      tmp_element%Gij(no_fl,2) = e_Gax(c_elem) 
      GAX = e_Gxa(c_elem)/GAA
      GXA = e_Gxa(c_elem)/GAA
    

    而不是:

      tmp_element%Gij(no_fl,1) = this%elOld(c_elem)%Gxa
      tmp_element%Gij(no_fl,2) = this%elOld(c_elem)%Gax
      GAX = tmp_element%Gij(no_fl,1)/GAA
      GXA = tmp_element%Gij(no_fl,2)/GAA
    

    程序变得明显变慢......(使用type(elemen):: tmp_element),这意味着访问简单数组e_Gxa(:)比访问类型数组中的复杂变量Gxa要慢,即此%elOld( :)%GXA。仅更改代码的几行可能会改变可见的执行时间。 我用gfortran检查了它,结果是一样的。以下是我正在讨论的完整代码的一部分:modskminv_fast.f90。 很抱歉发布完整模块,但是当我将代码拆分成小块时,我无法获得相同的结果。

1 个答案:

答案 0 :(得分:0)

错误显然在我身边。偶然我将所有复杂的数组声明为:

complex(16)

而不是

complex*16 or complex(kind=8)

很抱歉打扰你。现在,它就像一个魅力:)