3

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

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++ 程序来读取二进制文件,因此我必须使用流访问。

非常感谢任何建议。

4

3 回答 3

3

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

于 2013-04-12T01:53:04.587 回答
2

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

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)

确实,数组的遍历顺序并不是那么重要。I/O 操作会极大地减慢您的速度

PS 请不要在 Fortran 2003 中用 continue 结束你的循环,这很痛苦。

于 2013-04-12T08:36:08.673 回答
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
...
于 2013-04-12T07:18:30.500 回答