如何在FORTRAN中更快地写入二进制文件?

时间:2013-04-12 00:18:48

标签: fortran binary-data

我有以下代码用于写入二进制文件:

CALL system_clock(Time1, rate)
OPEN( 1, FILE='Test.bin', STATUS='UNKNOWN', ACCESS='STREAM')

DO 275 I=1,NDOF
  DO 274 J=1,UBW
    IF (S(I,J).NE.0) THEN
      WRITE (1) I
      WRITE (1) J+I-1
      WRITE (1) (S(I,J))
    ENDIF
  274 CONTINUE
275 CONTINUE

CLOSE(1)
CALL system_clock(Time2)
print *, "elapsed time: ", real(Time2-Time1) / real(rate)

我知道通过使用较少的WRITE语句,我可以使它更快。所以在循环中我使用以下代码并且它更快:

IF (S(I,J).NE.0) THEN 
WRITE (1) I, J+I-1,  (S(I,J)) 
ENDIF

有没有办法摆脱循环(因为它很耗时)或进行任何其他更改以获得更高效的代码?

请注意,我希望在写作中有I,J + I-1和S(I,J)的顺序(仅非零值)。此外,由于我使用C ++程序读取二进制文件,我必须使用流访问。

非常感谢任何建议。

3 个答案:

答案 0 :(得分:3)

您可以做的一件事是翻转处理数组的顺序。所以在你的do语句中只需用j切换i即可。这是因为数组S(i,j)是二维的,并且它存储在存储器中的方式对于访问速度非常重要。存储取决于所使用的编程语言标准,Fortran(与C相反,但与Matlab相同)使用列主要形式的数组存储。因此,访问内存的最有效方法是从第一行中的元素开始遍历遍历数组的每一列。

答案 1 :(得分:1)

我在生产代码中所做的是首先填充缓冲区,然后将其存储在一个写入命令中。它比只写列快得多,或者比写单个值快得多。有点像:

CALL system_clock(Time1, rate)

allocate the buffer with the sufficient size
offset = 0
buffer = 0

DO I=1,NDOF
  DO J=1,UBW
    IF (S(I,J) /= 0) THEN
      buffer(offset: offset + int_size-1) = transfer(I,buffer)
      offset = offset + int_size
      buffer(offset: offset + int_size-1) = transfer(J+I-1,buffer)
      offset = offset + int_size
      buffer(offset: offset + real_size-1) = transfer((S(I,J))
      offset = offset + real_size
    ENDIF
  end do
end do

OPEN( 1, FILE='Test.bin', STATUS='UNKNOWN', ACCESS='STREAM')

write (1) buffer(1:offset-1)

CLOSE(1)
CALL system_clock(Time2)
print *, "elapsed time: ", real(Time2-Time1) / real(rate)

实际上,遍历数组的顺序不是那么重要。的 The I/O operations is what slows you down enormously

P.S。请不要在Fortran 2003中使用continue来结束循环,这很痛苦。

答案 2 :(得分:0)

另一件事,你可以这么做,我称之为手动循环展开。

CALL system_clock(Time1, rate)
OPEN( 1, FILE='Test.bin', STATUS='UNKNOWN', ACCESS='STREAM')

DO 275 I=1,NDOF
  DO 274 J=1,UBW,2
    IF (S(I,J).NE.0) THEN
      WRITE (1) I, J+I-1, S(I,J), I, J+I, S(I,J+1)
    ENDIF
  274 CONTINUE
275 CONTINUE

CLOSE(1)
CALL system_clock(Time2)
print *, "elapsed time: ", real(Time2-Time1) / real(rate)

可以考虑更改循环顺序的选项(在Fortran中,由于矩阵的列主要存储,最左边的索引应该更快地更改)但我认为加速应该可以忽略不计,这是时间所需要的I / O。

编辑:如果UBW是奇数,你应该确保J到目前为止还没有避免数组超出范围错误。

...
IF ((S(I,J) .NE. 0) .AND. (J .LT. UBW)) THEN
  WRITE (1) I, J+I-1, S(I,J), I, J+I, S(I,J+1)
ELSE
  WRITE (1) I, J+I-1, S(I,J)
ENDIF
...