立即在控制台屏幕上的同一行上打印

Print immediately on same line on the console screen

我正在尝试在控制台屏幕上打印一些长期迭代进度的指示器。具体来说,我希望在控制台屏幕上显示这样的内容:

----|----|----|----|

这是,每次迭代后打印-(不是5的倍数),每5次迭代后打印|。这应该是实时发生的:每次迭代一个字符。

我尝试使用带有 ADVANCE='NO' 选项的 WRITE 语句,但所需的输出仅在所有程序执行后显示在屏幕上,或者在我使用另一个 WRITE 带有 ADVANCE='YES' 选项的语句。

这是我的代码:

PROGRAM TEST

  IMPLICIT NONE
  INTEGER :: i

  DO i = 1,20
     CALL SLEEP(2)
     IF (MOD(i,5).EQ.0) THEN
        WRITE(*,'(A)',ADVANCE='NO'), '|'
     ELSE
        WRITE(*,'(A)',ADVANCE='NO'), '-'
     END IF
  END DO

END PROGRAM TEST

我正在使用 SLEEP 子例程来模拟每次迭代都需要很多时间的事实。

flush 语句可用于表示已写入文件的数据 "made available"。该语句的含义仍然取决于处理器,但通常您会根据您的问题获得所需的行为。

flush 语句需要一个单元号。对于控制台的输出,您可以使用 ISO_FORTRAN_ENV 中的 OUTPUT_UNIT。 Intel Fortran 17.0 及更早版本应该会显示您想要的行为,对于当前的 ifort beta,您还需要在写入语句中使用 OUTPUT_UNIT(这可能是一个 compiler/compiler 运行时错误)。

(您还应修复 WRITE 语句中的语法错误。)

确实,您的设置在理论上应该有效(考虑到不同编译器等的不同行为)。上面使用 FLUSH(index) 语句的建议,使用与标准输出相关的索引甚至应该解决不同编译器之间的差异。但是,在某些环境中(更具体地说是集群和 HPC 超级计算机),这仍然不能解决您的问题。虽然 implementing a progress-bar myself 我 运行 遇到了同样的问题,但得出以下结论:

HPC systems may perform extensive buffering of data before output, to increase the efficiency of the machine (disk-writes are the slowest memory access option)…and as a result this can sometimes overrule our flush command. The progress-bar in turn will not show much progress until it is actually finished, at which point the entire bar will be shown at once. There are options to force the infrastructure not to use this buffering (and the system administrators in general will not appreciate this), for example by setting the compiler flag -assume nobuffered_stdout. So the best solution for HPC applications will be the construction of a slightly modified progress bar, where the carriage return is not used.

您可以通过 运行 桌面上的相同代码轻松测试它,其中 FLUSH 语句将为您提供预期的行为。

根据 IanH 和 DannyVanpoucke 关于 flush 语句的建议,这是一个按预期工作的代码版本:

PROGRAM TEST

  IMPLICIT NONE
  INTEGER :: i

  DO i = 1,20
     CALL SLEEP(2)
     IF (MOD(i,5).EQ.0) THEN
        WRITE(UNIT=6,FMT='(A)',ADVANCE='NO'), '|'
        FLUSH(UNIT=6)
     ELSE
        WRITE(UNIT=6,FMT='(A)',ADVANCE='NO'), '-'
        FLUSH(UNIT=6)
     END IF
  END DO

END PROGRAM TEST

正如问题下方的一些评论所指出的,似乎需要使用 flush 语句使缓冲区中的信息可用是一个英特尔 Fortran 特定问题。