【问题标题】:Why can Fortran allocate such large arrays?为什么 Fortran 可以分配这么大的数组?
【发布时间】:2015-12-18 11:53:55
【问题描述】:

我已经编写了一个科学代码,并且像往常一样,这归结为计算代数特征值方程中的系数:计算这些系数需要对多维数组进行积分,这会迅速大幅增加内存使用量。一旦计算出矩阵系数,就可以释放原始的预积分多维数组,并由智能求解器接管,因此内存使用不再是大问题。如您所见,存在瓶颈,在我的 64 位、4 核、8 线程、8GB 内存笔记本电脑上,程序由于内存不足而崩溃。

因此,我正在实施一个系统,通过限制 MPI 进程在计算某些特征值矩阵元素时可以执行的任务的大小来控制内存使用情况。完成后,他们将寻找剩余的工作,以便矩阵仍然被填充,但以更顺序和更少并行的方式。

因此,我检查了我可以分配多少内存,这就是混乱开始的地方:我分配大小为 8 字节的双精度对象(使用 sizeof(1) 检查)并查看分配状态。

虽然我有 8 GB 的可用内存来运行只有 1 个进程的测试,但我可以分配一个最大为 (40000,40000) 的数组,它对应于大约 13GB 的内存!因此,我的第一个问题是:这怎么可能?有这么多虚拟内存吗?

其次,我意识到我也可以为多个进程做同样的事情:最多 16 个进程可以同时分配这些海量数组!

这不可能吧?

有人知道为什么会这样吗?以及我是否做错了什么?

编辑:

这是产生上述奇迹的代码,至少在我的机器上是这样。但是,当我将数组的元素设置为某个值时,它确实会按应有的方式运行并崩溃——或者至少开始缓慢地运行 very,我猜这是因为虚拟内存速度慢用过吗?

program test_miracle
    use ISO_FORTRAN_ENV
    use MPI

    implicit none

    ! global variables
    integer, parameter :: dp = REAL64                                           ! double precision
    integer, parameter :: max_str_ln = 120                                      ! maximum length of filenames
    integer :: ierr                                                             ! error variable
    integer :: n_procs                                                          ! MPI nr. of procs

    ! start MPI
    call MPI_init(ierr)                                                         ! initialize MPI
    call MPI_Comm_size(MPI_Comm_world,n_procs,ierr)                             ! nr. MPI processes
    write(*,*) 'RUNNING MPI WITH', n_procs, 'processes'

    ! call asking for 6 GB
    call test_max_memory(6000._dp)
    call MPI_Barrier(MPI_Comm_world,ierr)

    ! call asking for 13 GB
    call test_max_memory(13000._dp)
    call MPI_Barrier(MPI_Comm_world,ierr)

    ! call asking for 14 GB
    call test_max_memory(14000._dp)
    call MPI_Barrier(MPI_Comm_world,ierr)

    ! stop MPI
    call MPI_finalize(ierr)

contains
    ! test whether maximum memory feasible
    subroutine test_max_memory(max_mem_per_proc)
        ! input/output
        real(dp), intent(in) :: max_mem_per_proc                                ! maximum memory per process

        ! local variables
        character(len=max_str_ln) :: err_msg                                    ! error message
        integer :: n_max                                                        ! maximum size of array
        real(dp), allocatable :: max_mem_arr(:,:)                               ! array with maximum size
        integer :: ierr                                                         ! error variable

        write(*,*) ' > Testing whether maximum memory per process of ',&
            &max_mem_per_proc/1000, 'GB is possible'

        n_max = ceiling(sqrt(max_mem_per_proc/(sizeof(1._dp)*1.E-6)))

        write(*,*) '   * Allocating doubles array of size', n_max

        allocate(max_mem_arr(n_max,n_max),STAT=ierr)
        err_msg = '   * cannot allocate this much memory. Try setting &
            &"max_mem_per_proc" lower'
        if (ierr.ne.0) then
            write(*,*) err_msg
            stop
        end if

        !max_mem_arr = 0._dp                                                     ! UNCOMMENT TO MAKE MIRACLE DISSAPEAR


        deallocate(max_mem_arr)

        write(*,*) '   * Maximum memory allocatable'
    end subroutine test_max_memory
end program test_miracle

保存在test.f90,随后编译运行

mpif90 test.f90 -o test && mpirun -np 2 ./test

【问题讨论】:

  • 你能发布一个重现这个奇迹的最小工作代码吗?
  • 尝试将所有内存设置为零 - 即数组 = 0.0。那会发生什么?我怀疑这是stackoverflow.com/questions/864416/are-some-allocators-lazy 的一种表现形式,这对于 HPC 来说是众所周知的痛苦......
  • @IanBush 我很确定这正是问题所在。事实上,我今天早些时候遇到了这个确切的问题。 OP,如果您一分配内存,就尝试将其设置为 0.0,它肯定会崩溃。

标签: memory fortran fortran90


【解决方案1】:

当您执行allocate 语句时,您在虚拟内存空间中保留了一个域。虚拟空间是物理内存 + 交换 + 可能由于某些 overcommit 可能性而导致的一些额外可能空间的总和,这将假设您不会使用所有保留。

但在您向其中写入内容之前,该内存尚未物理保留。当你往内存里写东西的时候,系统会在物理上为你分配相应的页面。 如果你不初始化你的数组,并且如果你的数组非常稀疏,那么可能有很多页面从未被写入,因此内存永远不会在物理上完全使用。

当您看到系统变慢时,可能是因为物理内存已满,系统正在将页面交换到磁盘。如果您在磁盘上有 8GB RAM 和 8GB 交换空间,则您的计算可以运行(非常缓慢...)

这种机制在 NUMA 环境中非常好,因为这种“首次接触策略”会将内存分配到靠近首先写入它的 CPU 的位置。 通过这种方式,您可以在 OpenMP 循环中初始化一个数组,以将内存物理放置在靠近将使用它的 CPU 的位置。

【讨论】:

  • 谢谢。这完美地解释了我的经验。
猜你喜欢
  • 2011-10-10
  • 2016-08-05
  • 1970-01-01
  • 2018-04-25
  • 2023-02-08
  • 2018-05-16
  • 2016-10-08
  • 1970-01-01
  • 2021-03-02
相关资源
最近更新 更多