强制提交到磁盘而不是缓冲区 - 系统资源耗尽
Forcing commit to disk rather than buffer - System resources exhausted
在之前的项目 () 之后,我启动了程序,运行 它在创建小数组(<2 GB RAM)的小数据文件上产生了有效结果。然而,对于更大的项目数据文件,阵列正在接近 10 GB。数据的读取和处理进展顺利
但是当涉及到将数据写回文件时,它没有写入磁盘,而是填满了内存缓冲区,耗尽了系统内存(32 GB RAM),然后机器锁定并重新启动。此结果在机器(笔记本电脑、台式机和虚拟机)之间是一致的,并且与完成该过程的存储设备(SSD、HDD、USB-HDD 或网络 HDD)无关。
所有系统都使用了大约 12-18 个月,i7 处理器,足够的 RAM 和磁盘 space,等等
Google 提供了 FLUSH 等建议,将环境变量 GFORTRAN_UNBUFFERED_ALL 设置为 1(或 'y' 或 'Y')和手动方法,例如关闭文件和然后使用 ACTION='append' 再次打开它以强制写入。
在这些方法中,close-n-open 方法是唯一明显有效的方法,但它只会导致内存填充比其他方法慢,最终系统会再次崩溃。
下面是一个没有任何干扰的写的例子:
program giant_array
use iso_fortran_env
implicit none
character(len=*), parameter :: csvfmt = '(*(f0.3,:,","))'
character(20) intval
character(200) line
integer(kind=int32) x, y, z, i, cnt
real(kind=real64), dimension(:,:,:,:), allocatable :: model
print *,
print *, "Allocating array and assigning values..."
print *,
call random_seed()
allocate(model(382,390,362,28))
call random_number(model)
print *, "Writing array to file..."
print *,
open(31, file="test.csv", status='replace', action='write')
cnt=0
! Write array to file:
do x = 1, 382
do y = 1, 390
do z = 1, 362
write(31, csvfmt) (model(x,y,z,i), i = 1, 28)
cnt=cnt+1
if((int(cnt/1000)*1000).eq.cnt) then
line = " Processing block grade "
write(intval,'(I12)') cnt
line = trim(line)//" "//trim(adjustl(intval))//"..."
write(*,'(A,A)', advance='no') achar(13), trim(line)
endif
enddo
enddo
enddo
close(31, status='keep')
end program
在执行过程中,您会注意到 test.csv 保持在 size=0,直到您终止程序。
即使在打开和关闭之间 'call SLEEP(1)',缓冲区的填充速度也比磁盘写入快,并且在作业完成之前系统崩溃。它也需要很长时间才能完成。
我找到了使用 fsync() 解决此问题的参考资料,但无法编译代码(我想我正在填充命令行参数)。代码如下,来自gcc.gnu.org:
! Declare the interface for POSIX fsync function
interface
function fsync (fd) bind(c,name="fsync")
use iso_c_binding, only: c_int
integer(c_int), value :: fd
integer(c_int) :: fsync
end function fsync
end interface
! Variable declaration
integer :: ret
! Opening unit 10
open (10,file="foo")
! ...
! Perform I/O on unit 10
! ...
! Flush and sync
flush(10)
ret = fsync(fnum(10))
! Handle possible error
if (ret /= 0) stop "Error calling FSYNC"
虽然其他人也遇到过这个问题,但我无法在任何地方找到解决方案。评论和博客文章表明,即使是 fsync() 方法也不一定总能奏效。
结果每次都是系统崩溃自重启
我猜一定有一种方法可以一次性将大文件写入磁盘,而无需过多的系统规格。
非常感谢。
已更新
代码更新如下,以测试 C++ _commit 语句强制从缓冲区到磁盘。与关闭然后重新打开方法一样工作 - 仍然会杀死机器。完全有可能我的实现还是有问题...
program giant_array
use iso_fortran_env
use iso_c_binding
implicit none
! Declare the interface for WIN32 _commit function
interface
function commit (fd) bind(c,name="_commit")
use iso_c_binding, only: c_int
integer(c_int), value :: fd
integer(c_int) :: commit
end function commit
end interface
character(len=*), parameter :: csvfmt = '(*(f0.3,:,","))'
character(20) intval
character(200) line
integer(kind=int32) error
integer(kind=int32) var, x, y, z, i, cnt
real(kind=real64), dimension(:,:,:,:), allocatable :: model
print *,
print *, "Allocating array and assigning values..."
print *,
call random_seed()
allocate(model(382,390,362,28))
call random_number(model)
print *, "Writing array to file..."
print *,
open(31, file="test.csv", status='replace', action='write')
cnt=0
! Write array to file:
do x = 1, 382
do y = 1, 390
do z = 1, 362
write(31, csvfmt) model(x,y,z,:)
cnt=cnt+1
if((int(cnt/1000)*1000).eq.cnt) then
line = " Processing block grade "
write(intval,'(I12)') cnt
line = trim(line)//" "//trim(adjustl(intval))//"..."
write(*,'(A,A)', advance='no') achar(13), trim(line)
flush(31)
error=commit(fnum(31))
endif
enddo
enddo
enddo
close(31, status='keep')
end program
如果您使用 Windows 10,如 "windows-10" 标记所示,那么我怀疑您显示的 fsync 代码编译失败的原因是 fsync() 是一个POSIX 函数,但在 Windows 上找不到。我依稀记得 Windows 有一个名为 _commit 的函数,它应该大致相当于 fsync。
这个问题似乎与编译器有关,而不是 OS、Windows 10.
为了进一步测试问题,我安装了个人版的FTN95,修改了代码,重新编译。代码如下:
program giant_array
implicit none
character(len=17), parameter :: csvfmt = '(500(f0.3,:,","))'
character(20) intval
character(200) line
character(1000) outline
integer(kind=4) x, y, z, cnt
real(kind=2), dimension(:,:,:,:), allocatable :: model
write(*,*)
write(*,*) "Allocating array and assigning values..."
write(*,*)
call random_seed()
allocate(model(28,382,390,362))
call random_number(model)
write(*,*) "Writing array to file..."
write(*,*)
open(31, file="test.csv", status='replace', action='write')
! Write array to file:
cnt=0
do x = 1, 382
do y = 1, 390
do z = 1, 362
write(outline, fmt=csvfmt) model(:,x,y,z)
write(31, '(a)') trim(outline)
cnt=cnt+1
if((int(cnt/1000)*1000).eq.cnt) then
line = " Processing record "
write(intval,'(I12)') cnt
line = trim(line)//" "//trim(adjustl(intval))//"..."
write(*,'(A,A)', advance='no') achar(13), trim(line)
endif
enddo
enddo
enddo
close(31, status='keep')
end program
使用 FTN95 编译的程序对系统没有不利影响,文件将毫无问题地写入磁盘,并且比使用 gfortran(gcc 版本 8.1.0)快得多。虽然这个答案没有解决问题,但它产生了一个有效的结果。
我会继续研究 gfortran 的通用解决方案。
在之前的项目 (
但是当涉及到将数据写回文件时,它没有写入磁盘,而是填满了内存缓冲区,耗尽了系统内存(32 GB RAM),然后机器锁定并重新启动。此结果在机器(笔记本电脑、台式机和虚拟机)之间是一致的,并且与完成该过程的存储设备(SSD、HDD、USB-HDD 或网络 HDD)无关。
所有系统都使用了大约 12-18 个月,i7 处理器,足够的 RAM 和磁盘 space,等等
Google 提供了 FLUSH 等建议,将环境变量 GFORTRAN_UNBUFFERED_ALL 设置为 1(或 'y' 或 'Y')和手动方法,例如关闭文件和然后使用 ACTION='append' 再次打开它以强制写入。
在这些方法中,close-n-open 方法是唯一明显有效的方法,但它只会导致内存填充比其他方法慢,最终系统会再次崩溃。
下面是一个没有任何干扰的写的例子:
program giant_array
use iso_fortran_env
implicit none
character(len=*), parameter :: csvfmt = '(*(f0.3,:,","))'
character(20) intval
character(200) line
integer(kind=int32) x, y, z, i, cnt
real(kind=real64), dimension(:,:,:,:), allocatable :: model
print *,
print *, "Allocating array and assigning values..."
print *,
call random_seed()
allocate(model(382,390,362,28))
call random_number(model)
print *, "Writing array to file..."
print *,
open(31, file="test.csv", status='replace', action='write')
cnt=0
! Write array to file:
do x = 1, 382
do y = 1, 390
do z = 1, 362
write(31, csvfmt) (model(x,y,z,i), i = 1, 28)
cnt=cnt+1
if((int(cnt/1000)*1000).eq.cnt) then
line = " Processing block grade "
write(intval,'(I12)') cnt
line = trim(line)//" "//trim(adjustl(intval))//"..."
write(*,'(A,A)', advance='no') achar(13), trim(line)
endif
enddo
enddo
enddo
close(31, status='keep')
end program
在执行过程中,您会注意到 test.csv 保持在 size=0,直到您终止程序。
即使在打开和关闭之间 'call SLEEP(1)',缓冲区的填充速度也比磁盘写入快,并且在作业完成之前系统崩溃。它也需要很长时间才能完成。
我找到了使用 fsync() 解决此问题的参考资料,但无法编译代码(我想我正在填充命令行参数)。代码如下,来自gcc.gnu.org:
! Declare the interface for POSIX fsync function
interface
function fsync (fd) bind(c,name="fsync")
use iso_c_binding, only: c_int
integer(c_int), value :: fd
integer(c_int) :: fsync
end function fsync
end interface
! Variable declaration
integer :: ret
! Opening unit 10
open (10,file="foo")
! ...
! Perform I/O on unit 10
! ...
! Flush and sync
flush(10)
ret = fsync(fnum(10))
! Handle possible error
if (ret /= 0) stop "Error calling FSYNC"
虽然其他人也遇到过这个问题,但我无法在任何地方找到解决方案。评论和博客文章表明,即使是 fsync() 方法也不一定总能奏效。
结果每次都是系统崩溃自重启
我猜一定有一种方法可以一次性将大文件写入磁盘,而无需过多的系统规格。
非常感谢。
已更新
代码更新如下,以测试 C++ _commit 语句强制从缓冲区到磁盘。与关闭然后重新打开方法一样工作 - 仍然会杀死机器。完全有可能我的实现还是有问题...
program giant_array
use iso_fortran_env
use iso_c_binding
implicit none
! Declare the interface for WIN32 _commit function
interface
function commit (fd) bind(c,name="_commit")
use iso_c_binding, only: c_int
integer(c_int), value :: fd
integer(c_int) :: commit
end function commit
end interface
character(len=*), parameter :: csvfmt = '(*(f0.3,:,","))'
character(20) intval
character(200) line
integer(kind=int32) error
integer(kind=int32) var, x, y, z, i, cnt
real(kind=real64), dimension(:,:,:,:), allocatable :: model
print *,
print *, "Allocating array and assigning values..."
print *,
call random_seed()
allocate(model(382,390,362,28))
call random_number(model)
print *, "Writing array to file..."
print *,
open(31, file="test.csv", status='replace', action='write')
cnt=0
! Write array to file:
do x = 1, 382
do y = 1, 390
do z = 1, 362
write(31, csvfmt) model(x,y,z,:)
cnt=cnt+1
if((int(cnt/1000)*1000).eq.cnt) then
line = " Processing block grade "
write(intval,'(I12)') cnt
line = trim(line)//" "//trim(adjustl(intval))//"..."
write(*,'(A,A)', advance='no') achar(13), trim(line)
flush(31)
error=commit(fnum(31))
endif
enddo
enddo
enddo
close(31, status='keep')
end program
如果您使用 Windows 10,如 "windows-10" 标记所示,那么我怀疑您显示的 fsync 代码编译失败的原因是 fsync() 是一个POSIX 函数,但在 Windows 上找不到。我依稀记得 Windows 有一个名为 _commit 的函数,它应该大致相当于 fsync。
这个问题似乎与编译器有关,而不是 OS、Windows 10.
为了进一步测试问题,我安装了个人版的FTN95,修改了代码,重新编译。代码如下:
program giant_array
implicit none
character(len=17), parameter :: csvfmt = '(500(f0.3,:,","))'
character(20) intval
character(200) line
character(1000) outline
integer(kind=4) x, y, z, cnt
real(kind=2), dimension(:,:,:,:), allocatable :: model
write(*,*)
write(*,*) "Allocating array and assigning values..."
write(*,*)
call random_seed()
allocate(model(28,382,390,362))
call random_number(model)
write(*,*) "Writing array to file..."
write(*,*)
open(31, file="test.csv", status='replace', action='write')
! Write array to file:
cnt=0
do x = 1, 382
do y = 1, 390
do z = 1, 362
write(outline, fmt=csvfmt) model(:,x,y,z)
write(31, '(a)') trim(outline)
cnt=cnt+1
if((int(cnt/1000)*1000).eq.cnt) then
line = " Processing record "
write(intval,'(I12)') cnt
line = trim(line)//" "//trim(adjustl(intval))//"..."
write(*,'(A,A)', advance='no') achar(13), trim(line)
endif
enddo
enddo
enddo
close(31, status='keep')
end program
使用 FTN95 编译的程序对系统没有不利影响,文件将毫无问题地写入磁盘,并且比使用 gfortran(gcc 版本 8.1.0)快得多。虽然这个答案没有解决问题,但它产生了一个有效的结果。
我会继续研究 gfortran 的通用解决方案。