Fortran 未格式化 I/O 优化

Fortran unformatted I/O optimization

我正在研究一组严重 I/O 绑定的 Fort运行 程序,因此我正在尝试对其进行优化。我在 multiple places 读到写整个数组比单个元素快,即 WRITE(10)arrDO i=1,n; WRITE(10) arr(i); ENDDO 快。但是,我不清楚我的案子在这方面会落在哪里。从概念上讲,我的代码是这样的:

OPEN(10,FILE='testfile',FORM='UNFORMATTED')
DO i=1,n
  [calculations to determine m values stored in array arr]
  WRITE(10) m
  DO j=1,m
    WRITE(10) arr(j)
  ENDDO
ENDDO

但是 m 可能每次通过 DO i=1,n 循环都会改变,因此写入整个数组 arr 不是一个选项。因此,折叠 DO 循环以进行写入将以 WRITE(10) arr(1:m) 结束,这与写入整个数组不同。这仍然可以提高写作速度吗?阅读呢?我可以在计算后分配一个大小为 m 的数组,将值分配给该数组,写入它,然后释放它,但这似乎太复杂了。

我还看到了关于隐式 DO 循环写入的不同信息,即 WRITE(10) (arr(j),j=1,m),关于它们是否 help/hurt 在 I/O 开销上。

我现在运行正在做一些测试,并打算更新我的观察结果。适用的其他建议

其他详细信息:

更新 我 运行 带有 WRITE(10) arr(1:m)READ(10) arr(1:m) 的代码。我的测试与这些一致,并显示 WRITE 代码的 运行 时间减少了大约 30%,输出文件也略小于原始文件大小的一半。对于第二个代码,读取文件,我让代码基本上什么都不做,只是读取文件来比较纯读取时间。这将 运行 时间减少了 30 倍。

避免通过多次WRITE()操作循环输出数组的要点是避免多次WRITE()操作。输出的数据是 all 数组的成员并不是特别重要。

通过单个 WRITE() 操作写入数组部分或整个数组是一个不错的选择。隐含的 DO 循环不会比显式外循环更糟糕,但它是否更好是编译器实现的问题。 (尽管我 期望 隐含的 DO 比外循环更好。)

如果您使用正常的未格式化(面向记录)I/O,您还会在数据本身前后写入一个记录标记。因此,您向每个数据项添加八个字节(通常)的开销,如果您的数字是双精度,这可以轻松(几乎)将写入光盘的数据加倍。 运行其他答案中提到的时间开销也很重要。

如果您使用未格式化的流,则上述参数不适用。

所以,使用

  WRITE (10) m
  WRITE (10) arr(1:m)

对于 gfortran,这比隐含的 DO 循环(即解决方案 WRITE (10) (arr(i),i=1,m))更快。

在建议的解决方案中,构建了一个数组描述符并通过一次调用将其传递给库。 I/O 然后可以更有效地完成,在您的情况下利用数据是连续的这一事实。

对于隐含的 DO 循环,gfortran 发出多个库调用, 开销。这可以优化,并且是一个长期存在的错误报告的主题,PR 35339,但是一些复杂的极端情况和可行的替代方案的存在阻止了它的优化。

我还建议在流访问中做 I/O,不是因为在 space 中节省了相当微不足道的内容(见上文),而是因为保持领先的记录标记在写入需要时保持最新一个寻求,这是额外的努力。

如果您的数据非常大,超过 ~ 2^31 字节,您可能 运行 进入带有记录标记的不同行为。 gfortran 在这种情况下使用子记录(与英特尔兼容),但它应该可以正常工作。我不知道波特兰在这种情况下做了什么。

对于读取,当然可以读取m,然后分配一个可分配数组,然后在一个READ语句中读取整个数组。