我有以下代码来写入二进制文件:
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++程序读取二进制文件,我必须使用流访问。
任何建议都非常感谢。
您可以做的一件事是颠倒处理数组的顺序。所以在你的do语句中,简单地把i换成j。这是因为数组S(i,j)是二维的,它在内存中的存储方式对访问速度非常重要。存储取决于所使用的编程语言标准,Fortran(与C相反,但与Matlab类似)以列为主形式使用数组存储。因此,访问内存的最有效方法是从第一行元素开始连续遍历数组的每一列。
我在生产代码中所做的是首先填充缓冲区,然后将其存储在一个写命令中。它比只写列或只写单个值要快得多。类似以下语句:
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操作极大地降低了你的速度。
注:请不要在Fortran 2003中以continue结束循环,这很伤人。
你还可以做一件事,我称之为手动循环展开。
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所需要的时间。
EDIT:如果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
...