循环访问fortran数组的复杂性

时间:2019-03-05 05:03:29

标签: arrays fortran time-complexity

最近,我正在研究访问fortran数组的复杂性。多亏了这些评论,这里我提供了完整的示例。

program main

    implicit none

    integer, parameter :: mp = SELECTED_REAL_KIND(15,307)
    integer, parameter :: Np=10, rep=100
    integer*8, parameter :: Ng(7) = (/1E3,1E4,1E5,1E6,1E7,1E8,1E9/)
    real(mp), allocatable :: x(:)
    real(mp) :: time1, time2
    integer*8 :: i,j,k, Ngj 
    real(mp) :: temp
    integer :: g

    ! print to screen
    print *, 'calling program main'

    do j=1,SIZE(Ng)   !test with different Ng

        !initialization with each Ng. Don't count for complexity.
        Ngj = Ng(j)
        if(ALLOCATED(x)) DEALLOCATE(x)
        ALLOCATE(x(Ngj))
        x = 0.0_mp

        !!===This is the part I want to check the complexity===!!
        call CPU_TIME(time1)

        do k=1,rep
            do i=1,Np
               call RANDOM_NUMBER(temp)
               g = floor( Ngj*temp ) + 1 

              x( g ) = x( g ) + 1.0_mp
            end do
        end do

        call CPU_TIME(time2)

        print *, 'Ng: ',Ngj,(time2-time1)/rep, '(sec)'

    end do

    ! print to screen
    print *, 'program main...done.'

contains

end program

我在一开始就认为它的复杂度是O(Np)。但这是Np = 10的时间测量:

 calling program main
 Ng:                  1000   7.9000000000000080E-007 (sec)
 Ng:                 10000   4.6000000000000036E-007 (sec)
 Ng:                100000   3.0999999999999777E-007 (sec)
 Ng:               1000000   4.8000000000001171E-007 (sec)
 Ng:              10000000   7.3999999999997682E-007 (sec)
 Ng:             100000000   2.1479999999999832E-005 (sec)
 Ng:            1000000000   4.5719999999995761E-005 (sec)
 program main...done.

此Ng依赖性非常慢,仅在非常大的Ng时才会出现,但在增加Np时并不占主导;增加Np只会在该时间比例上乘以一个常数因子。 同样,当我使用更复杂的子例程而不是随机数时,缩放斜率似乎也会增加。 验证了计算温度和g与Ng无关。 这种情况有两个问题:

  1. 基于注释,这种测量不仅包括预期的算术运算,还包括与内存高速缓存或编译器相关的成本。有没有更正确的方法来衡量复杂性?
  2. 关于注释中提到的问题,例如内存缓存,页面丢失或编译器,随着数组大小的增加,它们是否不可避免?还是有什么办法可以避免这些费用?

1 个答案:

答案 0 :(得分:0)

  
      
  1. 我如何理解这种复杂性?我错过了多少钱   占?我猜想访问元素的成本   数组确实取决于数组的大小。一些堆栈溢出   帖子说,对于某些语言,数组访问仅花费O(1)。一世   认为它也应该适合fortran,但是我不知道为什么   并非如此。
  2.   

除了您或多或少明确地向程序询问(执行循环,获取随机数等)外,还会发生许多事件,例如运行时环境的加载和输入/输出处理。为了做出有用的计时,您必须完全隔离代码与时间之间的关系,或者安排实际的计算要比其余代码花费更多的时间。

  
      
  1. 有什么办法可以避免这笔费用?
  2.   

这是回复1:-)

现在,为解决方案:我完成了您的示例,并使其运行了数亿次迭代。见下文:

>>> from http.client import responses
>>> responses[404]
'Not Found'

我使用program time_random integer, parameter :: rk = selected_real_kind(15) integer, parameter :: Ng = 100 real(kind=rk), dimension(Ng) :: x = 0 real(kind=rk) :: temp integer :: g, Np write(*,*) 'Enter number of loops' read(*,*) Np do i=1,Np call RANDOM_NUMBER(temp) g = floor( Ng*temp ) + 1 x(g) = x(g) + 1 end do write(*,*) x end program time_random 对其进行了编译,并使用了bash中的gfortran -O3 -Wall -o time_random time_random.f90函数对其进行了计时。请注意,这是非常粗糙的(并解释了为什么使迭代次数如此之大)。设置也非常简单:

time

您现在可以收集时序并观察线性复杂度。我的计算机每次迭代报告14 ns。

备注:

  1. 我使用for ii in 100000000 200000000 300000000 400000000 500000000 600000000 do time echo $ii | ./time_random 1>out done 来指定真正的种类。
  2. 我在循环后写selected_real_kind,以确保循环没有被优化。