【问题标题】:Fortran execute_command_line runtime error, depends on memory consumptionFortran execute_command_line 运行时错误,取决于内存消耗
【发布时间】:2019-03-12 11:46:18
【问题描述】:

我在尝试使用 Fortran 中的 execute_command_line 内部函数创建目录时遇到运行时错误。 Ifort (18.0.3 20180410) 和 gfortran (4.8.5) 都会出现该错误。 这是一个最小的例子,无论我使用什么编译标志都会失败:

PROGRAM directory_test

    IMPLICIT NONE

    INTEGER :: cstat, estat, i, j
    CHARACTER(LEN=100) :: cmsg

    REAL, DIMENSION(:,:), ALLOCATABLE :: field
    INTEGER, PARAMETER :: fieldsize = 80000

    allocate(field(fieldsize,fieldsize))
    do j=1, fieldsize
        do i=1, fieldsize
            field(i,j) = real(i+j)
        end do
    end do

    call execute_command_line('mkdir -p newdir', WAIT=.true., EXITSTAT=estat, CMDSTAT=cstat, CMDMSG=cmsg)

    write(*,*) 'estat: ', estat
    write(*,*) 'cstat: ', cstat
    write(*,*) 'cmsg:  ', cmsg

END PROGRAM directory_test

输出ifort:
状态:0
cstat: 124
cmsg:提供给 EXECUTE_COMMAND_LINE 的命令无效

输出 gfortran:
统计:-520880432
cstat: 1
cmsg:无法获取命令语言解释器的终止状态

关键是:只要数组足够小,程序就可以正常运行。对我来说,阈值大约是使用的物理内存的一半(如果您想尝试代码,请调整“fieldsize”的值)。如果数组大于那个值,就会发生错误。如果数组较小,则代码执行时不会出错并创建目录。 我用来测试的机器都有 2 个物理 CPU 和 128GB-256GB 的 RAM。
我做错了什么?

操作系统:Linux、Opensuse 42.3
外壳:bash
文件系统:Ext4

编辑:这个问题不是“execute_command_line()”独有的。尝试对“调用系统()”做同样的事情,我得到了类似的行为。如果原始方法因运行时错误而失败,则不会创建新目录。 在可用 RAM 低至 48GB 的​​较小/较旧双插槽机器上进行的其他测试产生了相同的结果。有些失败较早,有些几乎需要程序占用的整个物理内存才能失败。不幸的是,我现在没有要测试的单插槽机器。

【问题讨论】:

  • 我碰巧有相同的操作系统版本。我执行了代码并成功创建了目录。但在执行过程中,它使用了我 32GB 内存的大约 75%。只是cmsg 包含垃圾,这并不奇怪,因为estatcstat 都是0。它在gfortran 4.8.5 和ifort 16 中都成功了。在这台机器上我无法测试更大的尺寸。
  • 我可以确认 30GB 阵列的相同行为。当我在 ifort 中尝试运行时检查时,它 segfauletd,GCC 中的地址清理程序内存不足。我有足够的交换空间用于发送其他程序。

标签: bash fortran runtime-error


【解决方案1】:

因此,EXECUTE_COMMAND_LINE 以及 SYSTEM 内在函数在 Linux 上的实现方式是首先进程调用 fork() 系统调用,这会创建进程的克隆。然后子进程将调用 exec() ,它将该进程替换为要执行的新进程。

因此,即使第二个分叉的进程在被新的 exec() 进程替换之前非常短暂,系统仍然需要为原始进程和分叉的子进程提供足够的虚拟内存。在 Linux 上,如果您具有超级用户访问权限,则可以使用各种“过度使用”旋钮调整限制(作为物理内存的函数)。

如果可能,解决此问题的一种方法是在分配大量内存之前或在释放内存之后调用 EXECUTE_COMMAND_LINE。

【讨论】:

  • 子进程需要多少虚拟内存?父进程当前使用的数量相同?那么当fork过程超过mem+swap一半的时候就触发了问题?
  • @VladimirF:是的,最初 fork()ed 进程是父进程的克隆,因此它需要相同数量的内存。 Linux 默认允许 overcommit,即总虚拟内存可能超过物理内存 + 交换,但可以由管理员配置(或完全禁用)。
  • 首先overcommit的原因是fork+exec方式启动新进程;在大多数情况下,您不需要与分配的虚拟内存一样多的后备存储。偶尔过度使用是一个糟糕的选择,然后可怕的 OOM 杀手选择了一个受害者进程来杀死,人们抱怨。但是这个论点有两个硬币..
  • 事先做所有与文件系统相关的东西确实有效,这是我迄今为止使用的解决方法......实际上不知道它为什么有效。只是希望有一个真正的解决方案不会以这种方式限制我。但这对于我的同事来说更令人担忧,他在更改部分代码时非常不情愿*叹息
  • 好吧,我能够使用 /proc/sys/vm/overcommit_memory=2 和 /proc/sys/vm/overcommit_ratio=200 运行有问题的代码之一。我会牢记与此相关的风险
【解决方案2】:

您还在Intel Fortran for Linux forum 中发布了此消息。回复表明您的阵列分配正在用完所有可用的虚拟内存,为 EXECUTE_COMMAND_LINE 或替代方案留下的内存不足。三个不同的编译器都在您的示例中给出了错误 - NAGfor 对“虚拟内存不足”错误最有帮助。

【讨论】:

  • 这看起来很奇怪,运行完成正常。而且我的测试中有大量的交换空间。
  • 我对 Linux 的了解很少,但我知道基于内核构建参数的地址空间存在限制。
  • 虽然这似乎是一种可能的解释,但我仍然无法理解为什么会发生错误,尽管仍有大量可用内存。当我明天回到办公室时,我会去寻找占用大量虚拟内存的进程。欢迎提供解决方法。
猜你喜欢
  • 1970-01-01
  • 2011-03-27
  • 2021-02-28
  • 1970-01-01
  • 2016-12-30
  • 2014-07-15
  • 1970-01-01
  • 1970-01-01
  • 2011-11-25
相关资源
最近更新 更多