【问题标题】:Complexity for accessing fortran array in a loop在循环中访问 fortran 数组的复杂性
【发布时间】:2019-03-05 05:03:28
【问题描述】:

最近我正在研究访问 fortran 数组的复杂性。感谢 cmets,我在这里包含了完整的示例。

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 只是在该时间缩放上乘以一个常数因子。 此外,当我使用更复杂的子例程而不是随机数时,缩放斜率似乎会增加。 计算 temp 和 g 被证实与 Ng 无关。 这种情况有两个问题:

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

【问题讨论】:

  • 欢迎您,请拨打tour。您没有显示完整的代码,请参阅minimal reproducible example。您必须显示完整的代码,包括时间测量和编译器命令。编译器可能会优化您的代码,因为未使用结果。我们有重复的回合 stackoverflow.com/questions/47583729/…
  • 也有可能我不明白你显示的数字是什么意思,也许是时候进行一次迭代了。但是您必须明确地说明这一点并用您的代码说明这一点!另见How to Ask。不要忘记不仅有 CPU,还有内存和缓存,访问它们会花费不同的时间。
  • VladimirF 几乎可以肯定是正确的,您会看到由于缓存和可能的页面未命中而导致的成本增加。但是恐怕如果没有完整程序的更清晰的解释,就不可能肯定地说。并且不要使用非标准的、潜在的不可移植的 real*8 - 使用您清楚知道的种类,因为您在常量上使用了 _dp
  • 请记住,big-O 和渐近复杂度的整个结构是基于计算机的理论模型,其中涉及一些琐碎的问题,例如缓存大小、内存访问的时间一致性和一大堆其他的工程问题都被简单地假设了。观察到循环时间从~0.0s~0.0s,因为Ng 经历了 3 个数量级,这告诉我们有关它正在运行的计算机的一些信息,而根本没有关于操作的复杂性顺序。跨度>
  • @VladimirF 感谢 cmets。我只是用一个完整的例子代替。

标签: arrays fortran time-complexity


【解决方案1】:
  1. 我如何理解这种复杂性?我错过了什么代价 占?我猜访问一个元素的成本 数组确实取决于数组的大小。一些堆栈溢出 帖子说某些语言的数组访问成本仅为 O(1)。一世 认为它也应该适用于 fortran,但我不知道为什么会这样 并非如此。

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

  1. 有什么办法可以避免这种成本?

这是回复 1 :-)

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

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

我使用gfortran -O3 -Wall -o time_random time_random.f90 编译它,并使用来自bash 的time 函数对其计时。请注意,这是非常粗略的(并解释了为什么我将迭代次数设置得如此之大)。设置也很简单:

for ii in 100000000 200000000 300000000 400000000 500000000 600000000
do
time echo $ii | ./time_random 1>out
done

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

备注:

  1. 我使用selected_real_kind 来指定真正的种类。
  2. 我在循环之后写x 以确保循环不会被优化掉。

【讨论】:

  • 我认为问题是时间如何随 Ng 而不是 Np - 尽管我同意这并不完全清楚
  • 感谢@IanBush 的评论确实问题中的时间是Ng 的函数。
猜你喜欢
  • 2023-03-30
  • 2011-12-04
  • 1970-01-01
  • 2020-06-07
  • 1970-01-01
  • 1970-01-01
  • 2014-01-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多