Fortran未格式化的I / O优化

时间:2015-03-04 15:40:13

标签: performance io fortran

我正在研究一组严重受I / O限制的Fortran程序,因此我正在尝试优化它。我在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),它们是否有助于/损害I / O开销。

我现在正在进行几项测试,并打算根据我的观察进行更新。关于适用的其他建议

其他详情:

  • 第一个程序创建一个大文件,第二个程序读取它。而且,不,合并这两个程序并将所有内容保存在内存中并不是一个有效的选择。
  • 我正在使用未格式化的I / O,并且可以访问Portland Group和gfortran编译器。我的理解是PG通常更快,所以这就是我正在使用的。
  • 输出文件目前约为600 GB,代码需要几个小时才能运行。
  • 第二个程序(在文件中读取)似乎特别昂贵。我监视系统并发现它主要受CPU限制,即使我将代码减少到只读取文件,表明在读取每个值时所有I / O调用都有非常大的CPU开销。一个-AT-A-时间。
    • 编译器标志:-O3(高优化)-fastsse(各种性能增强,针对SSE硬件进行了优化)-Mipa =快速,内联(允许对编译器进行积极的过程间分析/优化)

更新 我使用WRITE(10) arr(1:m)READ(10) arr(1:m)运行了代码。我对这些测试表示同意,并且WRITE代码的运行时间减少了大约30%,输出文件也略小于原始大小的一半。对于第二个代码,在文件中读取,我做的代码基本上没什么,只是读取文件来比较纯读取时间。这将运行时间缩短了30倍。

2 个答案:

答案 0 :(得分:4)

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

通过单个WRITE()操作编写数组部分或整个数组是一个不错的选择。隐含的DO循环不能比显式的外循环更差,但它是否更好是编译器实现的问题。 (虽然我期待暗示 - DO优于外部循环。)

答案 1 :(得分:4)

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

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

所以,使用

  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,而不是因为空间节省相当微不足道(见上文),但是因为在编写时保持最新的主要记录标记需要寻求,这是额外的努力。

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

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