Memory management Fortran内存分配没有给出错误,但程序在初始化时被操作系统终止
给出下面提供的最小工作示例,您知道为什么内存分配步骤中不会出现内存分配错误吗?正如我所检查的,当我使用valgrind来运行代码,或者将参数source=0.0添加到内存分配语句中时,正如预期的那样,出现了内存分配错误 更新:我用最少的工作示例再现了该问题:Memory management Fortran内存分配没有给出错误,但程序在初始化时被操作系统终止,memory-management,fortran,Memory Management,Fortran,给出下面提供的最小工作示例,您知道为什么内存分配步骤中不会出现内存分配错误吗?正如我所检查的,当我使用valgrind来运行代码,或者将参数source=0.0添加到内存分配语句中时,正如预期的那样,出现了内存分配错误 更新:我用最少的工作示例再现了该问题: program memory_test implicit none double precision, dimension(:,:,:,:), allocatable :: sensitivity double preci
program memory_test
implicit none
double precision, dimension(:,:,:,:), allocatable :: sensitivity
double precision, allocatable :: sa(:)
double precision, allocatable :: sa2(:)
integer :: ierr,nnz
integer :: nx,ny,nz,ndata
nx = 50
ny = 50
nz = 100
ndata = 1600
allocate(sensitivity(nx,ny,nz,ndata),stat=ierr)
sensitivity = 1.0
nnz = 100000000
!allocate(sa(nnz),source=dble(0.0),stat=ierr)
allocate(sa(nnz),stat=ierr)
if(ierr /= 0) print*, 'Memory error!'
!allocate(sa2(nnz),source=dble(0.0),stat=ierr)
allocate(sa2(nnz),stat=ierr)
if(ierr /= 0) print*, 'Memory error!'
print*, 'Start initialization'
sa = 0.0
sa2 = 0.0
print*, 'End initialization'
end program memory_test
当我运行它时,我没有消息“内存错误!”打印,但有消息“启动初始化”,然后程序被操作系统终止。如果我只使用带有“source”参数的内存分配(如代码中的注释),那么我会收到消息“memory error!”
对于内存统计,“free”命令提供以下输出:
total used free shared buffers cached
Mem: 8169952 3630284 4539668 46240 1684 124888
-/+ buffers/cache: 3503712 4666240
Swap: 0 0 0
延伸评论而非回答: 在Fortran中,初始化具有特定的含义;它是指在声明时设置变量的值。那么这个
real :: myvar = 0.0
是初始化。而这些
real :: myvar
....
myvar = 0.0
不是。现在,也许与你报道的问题更相关的是,这份声明
isensit%sa(:) = 0.0
将值0.0
分配给数组部分的每个元素isensit%sa(:)
。这(一旦你习惯了)与我认为你想写的内容非常不同,即:
isensit%sa = 0.0
此版本将值0.0
分配给数组的每个元素isensit%sa
。由于数组部分(即使是包含数组中每个元素的部分)不是数组,Fortran编译器在处理赋值时可能会临时为该部分分配空间。当您考虑更一般的数组部分时,这可能是有意义的
我不太明白为什么在执行allocate
语句时,您认为空间没有分配,但我建议您先整理一下分配,然后再考虑。我猜临时分配给数组部分的空间,与数组本身消耗的空间一样多,可能会使程序崩溃,并导致您报告的行为
顺便说一句,你可以试试这个说法
allocate(isensit%sa(isensit%nnz),source=0.0,stat=ierr)
如果您的编译器是最新的,那么它应该在一条语句中进行分配并设置数组中的值
哦,还有一句毫无意义的话:宁愿
使用mpi
(或者使用mpi\u mod
或者你的安装喜欢的任何东西包括mpif.h
。这将阻止(许多)对mpi例程的调用与其要求不匹配可能导致的错误。使用例程关联意味着编译器可以检查参数匹配,但头文件的包含不匹配。您看到了linux使用的内存分配策略的行为。当您分配内存但尚未写入时,它会仅包含在虚拟内存中(注意,这也可能受到特定Fortran运行库的影响,但我不确定)。此内存存在于您的进程虚拟地址空间中,但没有任何实际的物理内存页支持。只有当您写入内存时,才会分配物理页,并且仅足以满足写入要求
考虑以下计划:
program test
implicit none
real,allocatable :: array(:)
allocate(array(1000000000)) !4 gb array
print *,'Check memory - 4 GB allocated'
read *
array(1:1000000) = 1.0
print *,'Check memory - 4 MB assigned'
read *
array(1000000:100000000) = 2.0
print *,'Check memory - 400 MB assigned'
read *
array = 5.0
print *,'Check memory - 4 GB assigned'
read *
end program
此程序分配4 GB内存,然后写入4 MB数组段、396 MB数组段(总写入=400 MB),最后写入整个数组(总写入=4 GB)。程序在每次写入之间暂停,以便查看内存使用情况
分配之后,第一次写入之前:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
29192 casey 20 0 3921188 1176 1052 S 0.0 0.0 0:00.00 fortranalloc
所有的内存都是虚拟的(VIRT),只有一小部分由物理内存(RES)支持
在4 MB写入之后:
29192 casey 20 0 3921188 5992 1984 S 0.0 0.0 0:00.00 fortranalloc
29192 casey 20 0 3921188 392752 1984 S 0.0 1.6 0:00.18 fortranalloc
在396 MB写入之后:
29192 casey 20 0 3921188 5992 1984 S 0.0 0.0 0:00.00 fortranalloc
29192 casey 20 0 3921188 392752 1984 S 0.0 1.6 0:00.18 fortranalloc
在4 GB写入之后:
29192 casey 20 0 3921188 3.727g 1984 S 56.6 15.8 0:01.88 fortranalloc
请注意,每次写入后,驻留内存都会增加以满足写入要求。这表明实际的物理内存分配仅在写入时发生,而不仅仅是在分配时发生,因此正常的allocate()
无法检测错误。当您将源
参数添加到分配
时,会发生写入,这会导致内存的完全物理分配,如果失败,则可以检测到错误
您可能看到的是在内存耗尽时调用的linux。当这种情况发生时,OOM Killer将使用一种算法来确定要杀死什么以释放内存,并且您的代码的行为使其很有可能被杀死。当您的写入导致可以满足的物理分配时,您的进程正在停止由内核调用。您可以在写入时看到它(由赋值引起),但由于上面详述的行为,无法看到分配。下面是调用
allocate()
的三种方法的比较:
这里使用的机器是Linux(x86_64),具有64-GB物理内存和64-GB交换磁盘。ulimit-v
显示“无限”。在所有情况下(method
=1,2,3),程序都会为L
>120引发一个错误,即物理内存和交换内存的总和。对于方法
=1,3,系统会引发一个错误
Operating system error: Cannot allocate memory
Allocation would exceed memory limit
而对于method
=2,stat=ierr
检测到一个错误。对于L
<120,程序继续运行,其中method
=2开始写入大量的0…无论如何,在这台机器上,allocate()
允许的最大内存量似乎受到物理+交换大小的限制(一个合理的结果),尽管ulimit-v
显示无限的虚拟内存
下面是使用
ulimit-v
限制allocate()
所允许的最大内存量的另一个测试。此程序分配4GB数组并为2GB赋值
program alloc_test
implicit none
real, allocatable :: a(:), b(:)
integer ierr, n
n = 500000000
allocate( a( n ), stat=ierr ) !! requests 2GB virtual memory
if ( ierr /= 0 ) stop "Memory error! (a)"
allocate( b( n ), stat=ierr ) !! requests 2GB virtual memory
if ( ierr /= 0 ) stop "Memory error! (b)"
print *, "before assignment (type any key)"
call system( "ps aux | grep a.out" )
read *
print *, "now writing values..."
a(:) = 0.0 !! request 2GB resident memory
print *, "after assignment (type any key)"
call system( "ps aux | grep a.out" )
read *
end
如果我直接执行/a.out
,此程序将在分配()处不停止运行。
。我们现在基于
那么我们有
STOP Memory error! (a)
如果我们把它限制在2.2GB
STOP Memory error! (b)
最后,如果我们将其设置为>4GB,则分配开始
before assignment (type any key)
<username> 12380 0.0 0.0 3918048 652 pts/1 S+ 07:59 0:00 ./a.out
now writing values...
after assignment (type any key)
<username> 12380 38.0 2.9 3918048 1953788 pts/1 S+ 07:59 0:00 ./a.out