gpt4 book ai didi

performance - 使用大量小文件改进 Fortran 格式的 I/O

转载 作者:行者123 更新时间:2023-12-01 06:05:07 25 4
gpt4 key购买 nike

让我们假设我对从模拟中编写监视器文件有以下要求:

  • 必须写入大量单个文件,通常以 10000 个
  • 为单位
  • 文件必须是人类可读的,即格式化的 I/O
  • 定期向每个文件添加一个新行。通常每 50 秒一次。
  • 必须几乎立即访问新数据,因此不能选择大型手动写入缓冲区
  • 我们在一个 Lustre 文件系统上,该系统似乎针对相反的情况进行了优化:顺序写入少量大文件。

  • 制定要求的不是我,所以很遗憾,讨论它们没有意义。我只想找到具有上述先决条件的最佳解决方案。
    我想出了一个小的工作示例来测试一些实现。这是迄今为止我能做的最好的事情:
    !===============================================================!
    ! program to test some I/O implementations for many small files !
    !===============================================================!
    PROGRAM iotest

    use types
    use omp_lib

    implicit none

    INTEGER(I4B), PARAMETER :: steps = 1000
    INTEGER(I4B), PARAMETER :: monitors = 1000
    INTEGER(I4B), PARAMETER :: cachesize = 10

    INTEGER(I8B) :: counti, countf, count_rate, counti_global, countf_global
    REAL(DP) :: telapsed, telapsed_global
    REAL(DP), DIMENSION(:,:), ALLOCATABLE :: density, pressure, vel_x, vel_y, vel_z
    INTEGER(I4B) :: n, t, unitnumber, c, i, thread
    CHARACTER(LEN=100) :: dummy_char, number
    REAL(DP), DIMENSION(:,:,:), ALLOCATABLE :: writecache_real

    call system_clock(counti_global,count_rate)

    ! allocate cache
    allocate(writecache_real(5,cachesize,monitors))
    writecache_real = 0.0_dp

    ! fill values
    allocate(density(steps,monitors), pressure(steps,monitors), vel_x(steps,monitors), vel_y(steps,monitors), vel_z(steps,monitors))
    do n=1, monitors
    do t=1, steps
    call random_number(density(t,n))
    call random_number(pressure(t,n))
    call random_number(vel_x(t,n))
    call random_number(vel_y(t,n))
    call random_number(vel_z(t,n))
    end do
    end do

    ! create files
    do n=1, monitors
    write(number,'(I0.8)') n
    dummy_char = 'monitor_' // trim(adjustl(number)) // '.dat'
    open(unit=20, file=trim(adjustl(dummy_char)), status='replace', action='write')
    close(20)
    end do

    call system_clock(counti)

    ! write data
    c = 0
    do t=1, steps
    c = c + 1
    do n=1, monitors
    writecache_real(1,c,n) = density(t,n)
    writecache_real(2,c,n) = pressure(t,n)
    writecache_real(3,c,n) = vel_x(t,n)
    writecache_real(4,c,n) = vel_y(t,n)
    writecache_real(5,c,n) = vel_z(t,n)
    end do
    if(c .EQ. cachesize .OR. t .EQ. steps) then
    !$OMP PARALLEL DEFAULT(SHARED) PRIVATE(n,number,dummy_char,unitnumber, thread)
    thread = OMP_get_thread_num()
    unitnumber = thread + 20
    !$OMP DO
    do n=1, monitors
    write(number,'(I0.8)') n
    dummy_char = 'monitor_' // trim(adjustl(number)) // '.dat'
    open(unit=unitnumber, file=trim(adjustl(dummy_char)), status='old', action='write', position='append', buffered='yes')
    write(unitnumber,'(5ES25.15)') writecache_real(:,1:c,n)
    close(unitnumber)
    end do
    !$OMP END DO
    !$OMP END PARALLEL
    c = 0
    end if
    end do

    call system_clock(countf)
    call system_clock(countf_global)
    telapsed=real(countf-counti,kind=dp)/real(count_rate,kind=dp)
    telapsed_global=real(countf_global-counti_global,kind=dp)/real(count_rate,kind=dp)
    write(*,*)
    write(*,'(A,F15.6,A)') ' elapsed wall time for I/O: ', telapsed, ' seconds'
    write(*,'(A,F15.6,A)') ' global elapsed wall time: ', telapsed_global, ' seconds'
    write(*,*)

    END PROGRAM iotest

    主要特性是: OpenMP 并行化和手动写入缓冲区。
    以下是具有 16 个线程的 Lustre 文件系统上的一些时序:
  • 缓存大小 = 5:I/O 的挂墙时间:991.627404 秒
  • 缓存大小 = 10:I/O 的挂墙时间:415.456265 秒
  • 缓存大小 = 20:I/O 的挂墙时间:93.842964 秒
  • 缓存大小 = 50:I/O 的挂墙时间:79.859099 秒
  • 缓存大小 = 100:I/O 的挂墙时间:23.937832 秒
  • 缓存大小 = 1000:I/O 的挂墙时间:10.472421 秒

  • 引用具有停用 HDD 写入缓存的本地工作站 HDD 上的结果,16 个线程:
  • 缓存大小 = 1:I/O 的挂墙时间:5.543722 秒
  • 缓存大小=2:I/O 的经过墙时间:2.791811 秒
  • 缓存大小=3:I/O 的耗时:1.752962 秒
  • 缓存大小=4:I/O 的耗时:1.630385 秒
  • 缓存大小 = 5:I/O 的挂墙时间:1.174099 秒
  • 缓存大小=10:I/O 的经过墙时间:0.700624 秒
  • 缓存大小 = 20:I/O 的经过墙时间:0.433936 秒
  • 缓存大小=50:I/O 的墙时间:0.425782 秒
  • 缓存大小 = 100:I/O 的挂墙时间:0.227552 秒

  • 正如您所看到的,与普通 HDD 相比,Lustre 文件系统上的实现仍然慢得令人尴尬,我需要巨大的缓冲区大小来将 I/O 开销降低到可以接受的程度。这将意味着输出落后于之前制定的要求。
    另一种有前途的方法是在连续写入之间让单元保持打开状态。不幸的是,在没有 root 权限的情况下,同时打开的单元数量通常被限制在 1024-4096。所以这不是一个选项,因为文件数量可能超过这个限制。

    如何在满足要求的同时进一步降低 I/O 开销?

    编辑 1
    从与 Gilles 的讨论中,我了解到即使使用普通用户权限也可以调整 luster 文件系统。所以我尝试按照建议将 strip 计数设置为 1(这已经是默认设置)并将 strip 大小减小到支持的最小值 64k(默认值为 1M)。但是,这并没有提高我的测试用例的 I/O 性能。如果有人对更合适的文件系统设置有其他提示,请告诉我。

    最佳答案

    对于所有受小文件性能困扰的人来说,新的 2.11 版 sluster 允许将小文件直接存储在 MDT 上,从而缩短了访问时间。

    http://cdn.opensfs.org/wp-content/uploads/2018/04/Leers-Lustre-Data_on_MDT_An_Early_Look_DDN.pdf
    lfs setstripe -E 1M -L mdt -E -1 fubar fill 在 MDT 上的目录 fubar 中存储所有文件的第一兆字节

    关于performance - 使用大量小文件改进 Fortran 格式的 I/O,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41169581/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com