Memory management 仅使用非常大的预分配阵列的一小部分

Memory management 仅使用非常大的预分配阵列的一小部分,memory-management,fortran,allocation,Memory Management,Fortran,Allocation,当我们在Fortran或C中分配数组时,我的理解是,内存首先分配在所谓的虚拟内存中,而物理内存仅在我们将数据写入(数组的某个部分)时分配(例如,基于此)。这是否意味着,如果我们分配一个非常大的数组(比如说10^9个元素),并且只使用其中的一小部分(比如前10^6个元素),那么后面的数组是否只需要物理内存?如果是这样,利用此功能在一个非常大的预先分配的阵列中容纳未知(但不是太大)大小的数据实际上没有问题吗 例如,下面的Fortran代码首先分配一个大小为10^9的大型数组,将数据写入前10^6个元

当我们在Fortran或C中分配数组时,我的理解是,内存首先分配在所谓的虚拟内存中,而物理内存仅在我们将数据写入(数组的某个部分)时分配(例如,基于此)。这是否意味着,如果我们分配一个非常大的数组(比如说10^9个元素),并且只使用其中的一小部分(比如前10^6个元素),那么后面的数组是否只需要物理内存?如果是这样,利用此功能在一个非常大的预先分配的阵列中容纳未知(但不是太大)大小的数据实际上没有问题吗

例如,下面的Fortran代码首先分配一个大小为10^9的大型数组,将数据写入前10^6个元素,然后执行重新分配以调整数组大小

integer, allocatable :: a(:)
integer :: nlarge, nsmall, i

nlarge = 1000000000  !! 10^9
nsmall =    1000000  !! 10^6 

allocate( a( nlarge ) )   !! allocate in virtual memory

print *, "after allocation"
call system( "ps aux | grep a.out" )

do i = 1, nsmall
    a( i ) = i     !! write actual data (we assume that "nsmall" is not known a priori)
enddo

print *, "after assignment"
call system( "ps aux | grep a.out" )

a = a( 1 : nsmall )    !! adjust the array size by reallocation

print *, "after reallocation"
call system( "ps aux | grep a.out" )
我的机器(带有gfortran的Linux x86_64)上的输出是

这表明只使用了约5 MB的物理内存。是否可以利用此功能来容纳未知大小(但低于物理内存大小)的临时数据

编辑

更具体地说,我假设的系统是一个典型的工作站,运行Linux x86_64(例如CentOS),具有数十GB的RAM,程序是用Fortran编写的。这个问题的动机是,当我希望将未知大小的数据存储到一个数组中时,我通常需要以某种方式知道它的大小,并适当地分配一个数组。然而,这种方法有点乏味,除非我们有一个内置的动态数组。通常,这种情况发生在两种情况下:(1)从包含未知大小数据的外部文件读取数据时;(2)在多维循环中收集符合特定条件的数据时。在案例1中,我们通常扫描一个文件两次(一次获取数据大小,下一次读取数据),或者预先分配一个足够大的数组作为缓冲区。因此,我感兴趣的是虚拟内存系统是否允许分配非常大的阵列(而不太关心大小),从而有助于简化这项任务

然而,通过更多的实验,我知道这种方法是相当有限的。。。例如,如果我按如下方式更改阵列的大小,
ifort
会抱怨~80 GB以上的“虚拟内存不足”,这可能对应于我的系统上物理内存+交换区域的总和。所以,虽然“ulimit-a”说虚拟内存是“无限的”,但实际上它似乎不是无限的

! compiled with: ifort -heap-arrays -assume realloc_lhs 
use iso_fortran_env, only: long => int64
integer, allocatable :: a(:)
integer(long) :: nlarge, nsmall, i

! nlarge = 10_long**9   !! OK: 4 GB                                              
! nlarge = 10_long**10   !! OK: 40 GB                                            
nlarge = 2 * 10_long**10   !! OK: 80 GB                                         
! nlarge = 3 * 10_long**10   !! NG: insufficient virtual memory (120 GB)         
! nlarge = 4 * 10_long**10   !! NG: insufficient virtual memory (160 GB)         
! nlarge = 10_long**11    ! NG: insufficient virtual memory (400 GB)             

nsmall = 10**6   !! 4 MB   
结论:似乎最好使用传统方法(即,分配具有必要大小的数组,或根据需要重复重新分配可分配数组,或使用用户定义的动态数组)。我为这个微不足道的结论感到抱歉

当我们用Fortran或C语言分配一个数组时,我的理解是,内存首先分配在所谓的虚拟内存中,而物理内存只在我们将数据写入(数组的某一部分)时分配

这是您的操作系统可能选择做的一件事。不能保证它实际上不会为所有保留内存、映射和物理内存添加实际的页表条目

事实上,C或Fortran并没有告诉你如何获得内存,从哪里来,或者操作系统如何处理如何获得内存。您混淆了您的语言指定的内容、标准库如何处理内存请求以及底层操作系统如何将物理内存映射到进程地址空间——事实上,在没有MMU(内存管理单元)的系统上,您可以运行完美的C代码,但所有内存地址实际上都是物理的

这是否意味着,如果我们分配一个非常大的数组(比如说10^9个元素),并且只使用其中的一小部分(比如前10^6个元素),那么后面的数组是否只需要物理内存

小心点。同样,这要由操作系统来实现。操作系统可能(并且通常会)实现“惰性映射”功能,但仍然不会给您比物理可用内存多得多的内存

另外,请记住,至少对于32位操作系统,存在相关的内存空间限制:32位进程的内存空间不能超过2GB,这意味着您不能有10^9个32位整数

如果是这样,利用此功能在一个非常大的预先分配的阵列中容纳未知(但不是太大)大小的数据实际上没有问题吗

这实际上是一个问题,因为操作系统可能不会给你那么多内存。此外,除了花费的时间之外,以后获得更多的内存(参见第页)并没有什么真正的缺点;操作系统必须找到空闲页面,并将它们映射到您的进程空间,还可能重新映射以前的页面

当我们用Fortran或C语言分配一个数组时,我的理解是,内存首先分配在所谓的虚拟内存中,而物理内存只在我们将数据写入(数组的某一部分)时分配

这是您的操作系统可能选择做的一件事。不能保证它实际上不会为所有保留内存、映射和物理内存添加实际的页表条目

事实上,C或Fortran并没有告诉你如何获得内存,从哪里来,或者操作系统如何处理如何获得内存。您混淆了您的语言指定的内容、标准库如何处理内存请求以及底层操作系统如何将物理内存映射到进程地址空间——事实上,在没有MMU(内存管理单元)的系统上,您可以运行完美的C代码,但所有内存地址实际上都是物理的

这是否意味着,如果我们分配一个非常大的数组(比如说10^9个元素),并且只使用其中的一小部分(比如前10^6个元素),那么后面的数组是否只需要物理内存

小心点。同样,这取决于操作系统的实现
! compiled with: ifort -heap-arrays -assume realloc_lhs 
use iso_fortran_env, only: long => int64
integer, allocatable :: a(:)
integer(long) :: nlarge, nsmall, i

! nlarge = 10_long**9   !! OK: 4 GB                                              
! nlarge = 10_long**10   !! OK: 40 GB                                            
nlarge = 2 * 10_long**10   !! OK: 80 GB                                         
! nlarge = 3 * 10_long**10   !! NG: insufficient virtual memory (120 GB)         
! nlarge = 4 * 10_long**10   !! NG: insufficient virtual memory (160 GB)         
! nlarge = 10_long**11    ! NG: insufficient virtual memory (400 GB)             

nsmall = 10**6   !! 4 MB