Fortran 未格式化 I/O 优化
Fortran unformatted I/O optimization
我正在研究一组严重 I/O 绑定的 Fort运行 程序,因此我正在尝试对其进行优化。我在 multiple places 读到写整个数组比单个元素快,即 WRITE(10)arr
比 DO 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 开销上。
我现在运行正在做一些测试,并打算更新我的观察结果。适用的其他建议
其他详细信息:
- 第一个程序创建一个大文件,第二个程序读取它。而且,不,合并这两个程序并将所有内容保存在内存中不是一个有效的选择。
- 我正在使用未格式化的 I/O 并且可以访问 Portland Group 和 gfort运行 编译器。据我了解,PG 通常速度更快,所以这就是我正在使用的。
- 输出文件目前约为 600 GB,代码需要几个小时才能完成 运行。
- 第二个程序(读入文件)的开销似乎特别大。我监视了系统,发现它主要是 CPU-bound,即使我将代码减少到只读文件,这表明所有 [=] 的开销非常大 CPU 52=] 每次读取一个值时调用。
- 编译器标志:-O3(高度优化)-fastsse(各种性能增强,针对 SSE 硬件进行了优化)-Mipa=fast,inline(在编译器上启用积极的过程间analysis/optimization)
更新
我 运行 带有 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
语句中读取整个数组。
我正在研究一组严重 I/O 绑定的 Fort运行 程序,因此我正在尝试对其进行优化。我在 multiple places 读到写整个数组比单个元素快,即 WRITE(10)arr
比 DO 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 开销上。
我现在运行正在做一些测试,并打算更新我的观察结果。适用的其他建议
其他详细信息:
- 第一个程序创建一个大文件,第二个程序读取它。而且,不,合并这两个程序并将所有内容保存在内存中不是一个有效的选择。
- 我正在使用未格式化的 I/O 并且可以访问 Portland Group 和 gfort运行 编译器。据我了解,PG 通常速度更快,所以这就是我正在使用的。
- 输出文件目前约为 600 GB,代码需要几个小时才能完成 运行。
- 第二个程序(读入文件)的开销似乎特别大。我监视了系统,发现它主要是 CPU-bound,即使我将代码减少到只读文件,这表明所有 [=] 的开销非常大 CPU 52=] 每次读取一个值时调用。
- 编译器标志:-O3(高度优化)-fastsse(各种性能增强,针对 SSE 硬件进行了优化)-Mipa=fast,inline(在编译器上启用积极的过程间analysis/optimization)
更新
我 运行 带有 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
语句中读取整个数组。