NVMe SSD의 GFortran 비 포맷 I / O 처리량

MechEng

특히 NVMe SSD에서 작업 할 때 (G) Fortran을 사용하여 포맷되지 않은 순차적 I / O 처리량을 개선 할 수있는 방법을 이해하도록 도와주세요.

저는 약간의 테스트 프로그램을 작성했습니다.이 게시물의 하단을 참조하십시오. 이것이하는 일은 하나 이상의 파일을 병렬로 열고 (OpenMP) 난수 배열을 그 안에 쓰는 것입니다. 그런 다음 시스템 캐시를 플러시합니다 (루트 필요, 그렇지 않으면 읽기 테스트가 메모리에서 읽을 가능성이 가장 높음). 파일을 열고 파일을 읽습니다. 시간은 벽 시간 (I / O 관련 시간 만 포함)으로 측정되며 성능 수치는 MiB / s로 제공됩니다. 프로그램은 중단 될 때까지 반복됩니다.

테스트에 사용하는 하드웨어는 2 개의 PCIe 3.0 레인을 통해 연결된 Samsung 970 Evo Plus 1TB SSD입니다. 따라서 이론적으로 ~ 1500MiB / s의 순차 읽기 및 쓰기가 가능해야합니다. "dd if = / dev / zero of =. / testfile bs = 1G count = 1 oflag = direct"로 미리 테스트하면 ~ 750MB / s가됩니다. 그다지 좋지는 않지만 Gfortran으로 얻는 것보다 여전히 좋습니다. 그리고 당신이 묻는 사람에 따라 dd는 어쨌든 벤치마킹에 사용해서는 안됩니다. 이것은 하드웨어가 이론상 더 많은 것을 할 수 있는지 확인하기위한 것입니다.

내 코드의 결과는 파일 크기가 클수록 더 좋아지는 경향이 있지만 1GiB에서도 쓰기 약 200MiB / s, 읽기 420MiB / s로 제한됩니다. 더 많은 스레드 (예 : 4)를 사용하면 쓰기 속도가 약간 증가하지만 약 270MiB / s에 불과합니다. 벤치 마크 실행 시간을 짧게 유지하고 테스트 사이에 SSD가 휴식을 취할 수 있도록했습니다.

단일 스레드만으로도 PCIe 3.0 레인 2 개에 해당하는 대역폭을 포화시킬 수 있어야한다는 인상을 받았습니다. 적어도 포맷되지 않은 I / O를 사용하는 경우. 코드는 CPU에 제한되지 않는 것 같습니다. top은 "values"필드의 할당 및 초기화를 루프 밖으로 옮기면 단일 코어에서 50 % 미만의 사용량을 보여줍니다. 적어도 5 배 더 높은 수치를보고 싶다는 점을 감안하면 여전히 전반적인 성능에 좋지 않습니다.
또한 열린 문에 access = stream을 사용하려고 시도했지만 소용이 없었습니다.

그래서 무엇이 문제인 것 같습니까?
내 코드가 잘못되었거나 최적화되지 않았습니까? 내 기대치가 너무 높습니까?

사용 된 플랫폼 :
Opensuse Leap 15.1, Kernel 4.12.14-lp151.28.36-default
2x AMD Epyc 7551, Supermicro H11DSI, Samsung 970 Evo Plus 1TB (2xPCIe 3.0)
gcc 버전 8.2.1, 컴파일러 옵션 : -ffree-line-length- 없음 -O3 -ffast-math -funroll-loops -flto

MODULE types
    implicit none
    save

    INTEGER, PARAMETER  :: I8B = SELECTED_INT_KIND(18)
    INTEGER, PARAMETER  :: I4B = SELECTED_INT_KIND(9)
    INTEGER, PARAMETER  :: SP = KIND(1.0)
    INTEGER, PARAMETER  :: DP = KIND(1.0d0)

END MODULE types

MODULE parameters
    use types
    implicit none
    save

    INTEGER(I4B) :: filesize ! file size in MiB
    INTEGER(I4B) :: nthreads ! number of threads for parallel ececution
    INTEGER(I4B) :: alloc_size ! size of the allocated data field

END MODULE parameters



PROGRAM iometer
    use types
    use parameters
    use omp_lib

    implicit none

    CHARACTER(LEN=100) :: directory_char, filesize_char, nthreads_char
    CHARACTER(LEN=40)  :: dummy_char1
    CHARACTER(LEN=110) :: filename
    CHARACTER(LEN=10)  :: filenumber
    INTEGER(I4B) :: thread, tunit, n
    INTEGER(I8B) :: counti, countf, count_rate
    REAL(DP) :: telapsed_read, telapsed_write, mib_written, write_speed, mib_read, read_speed
    REAL(SP), DIMENSION(:), ALLOCATABLE :: values

    call system_clock(counti,count_rate)

    call getarg(1,directory_char)
    dummy_char1 = ' directory to test:'
    write(*,'(A40,A)') dummy_char1, trim(adjustl(directory_char))

    call getarg(2,filesize_char)
    dummy_char1 = ' file size (MiB):'
    read(filesize_char,*) filesize
    write(*,'(A40,I12)') dummy_char1, filesize

    call getarg(3,nthreads_char)
    dummy_char1 = ' number of parallel threads:'
    read(nthreads_char,*) nthreads
    write(*,'(A40,I12)') dummy_char1, nthreads

    alloc_size = filesize * 262144

    dummy_char1 = ' allocation size:'
    write(*,'(A40,I12)') dummy_char1, alloc_size

    mib_written = real(alloc_size,kind=dp) * real(nthreads,kind=dp) / 1048576.0_dp
    mib_read = mib_written

    CALL OMP_SET_NUM_THREADS(nthreads)
    do while(.true.)
        !$OMP PARALLEL default(shared) private(thread, filename, filenumber, values, tunit)

        thread = omp_get_thread_num()
        write(filenumber,'(I0.10)') thread
        filename = trim(adjustl(directory_char)) // '/' // trim(adjustl(filenumber)) // '.temp'

        allocate(values(alloc_size))
        call random_seed()
        call RANDOM_NUMBER(values)
        tunit = thread + 100

        !$OMP BARRIER
        !$OMP MASTER
        call system_clock(counti)
        !$OMP END MASTER
        !$OMP BARRIER

        open(unit=tunit, file=trim(adjustl(filename)), status='replace', action='write', form='unformatted')
        write(tunit) values
        close(unit=tunit)

        !$OMP BARRIER
        !$OMP MASTER
        call system_clock(countf)
        telapsed_write = real(countf-counti,kind=dp)/real(count_rate,kind=dp)
        write_speed = mib_written/telapsed_write
        !write(*,*) 'write speed (MiB/s): ', write_speed
        call execute_command_line ('echo 3 > /proc/sys/vm/drop_caches', wait=.true.)
        call system_clock(counti)
        !$OMP END MASTER
        !$OMP BARRIER

        open(unit=tunit, file=trim(adjustl(filename)), status='old', action='read', form='unformatted')
        read(tunit) values
        close(unit=tunit)

        !$OMP BARRIER
        !$OMP MASTER
        call system_clock(countf)
        telapsed_read = real(countf-counti,kind=dp)/real(count_rate,kind=dp)
        read_speed = mib_read/telapsed_read
        write(*,'(A29,2F10.3)') ' write / read speed (MiB/s): ', write_speed, read_speed
        !$OMP END MASTER
        !$OMP BARRIER
        deallocate(values)
        !$OMP END PARALLEL

        call sleep(1)

    end do

END PROGRAM iometer
Janneb

코드의 실수는 계산 mib_written에서 real(sp)변수 의 크기 (4 바이트) 를 고려하는 것을 잊었다는 것 입니다. 따라서 결과는 4의 요소가 너무 낮습니다. 예를 들어 다음과 같이 계산하십시오.

mib_written = filesize * nthreads

GFortran에 특정한 일부 사소한 nits :

  • 반복적으로 호출하지 마십시오 random_seed. 특히 각 스레드에서 호출하지 마십시오 . 호출하려면 프로그램 시작시 한 번 호출하십시오.
  • 를 사용 open(newunit=tunit, ...)하여 컴파일러 런타임이 각 파일에 고유 한 단위 번호를 할당하도록 할 수 있습니다 .
  • 당신은 '표준'64 비트 정수 / 부동 소수점 종류를 원한다면, 당신은 변수를 사용할 수 있습니다 int64real64로부터 iso_fortran_env고유 모듈.
  • 더 큰 파일로 테스트하려면 alloc_size종류의 int64.
  • get_command_argument비표준 대신 표준 내장 함수를 사용하십시오 getarg.
  • access='stream' 레코드 길이 마커를 처리 할 필요가 없기 때문에 기본값 (순차)보다 약간 빠릅니다.

이러한 수정 사항이 포함 된 테스트 프로그램 (및 parameters기본 프로그램으로 접힌 모듈)은 다음과 같습니다.

PROGRAM iometer
  use iso_fortran_env
  use omp_lib

  implicit none

  CHARACTER(LEN=100) :: directory_char, filesize_char, nthreads_char
  CHARACTER(LEN=40)  :: dummy_char1
  CHARACTER(LEN=110) :: filename
  CHARACTER(LEN=10)  :: filenumber
  INTEGER :: thread, tunit
  INTEGER(int64) :: counti, countf, count_rate
  REAL(real64) :: telapsed_read, telapsed_write, mib_written, write_speed, mib_read, read_speed
  REAL, DIMENSION(:), ALLOCATABLE :: values

  INTEGER :: filesize ! file size in MiB
  INTEGER :: nthreads ! number of threads for parallel ececution
  INTEGER(int64) :: alloc_size ! size of the allocated data field


  call system_clock(counti,count_rate)

  call get_command_argument(1, directory_char)
  dummy_char1 = ' directory to test:'
  write(*,'(A40,A)') dummy_char1, trim(adjustl(directory_char))

  call get_command_argument(2, filesize_char)
  dummy_char1 = ' file size (MiB):'
  read(filesize_char,*) filesize
  write(*,'(A40,I12)') dummy_char1, filesize

  call get_command_argument(3, nthreads_char)
  dummy_char1 = ' number of parallel threads:'
  read(nthreads_char,*) nthreads
  write(*,'(A40,I12)') dummy_char1, nthreads

  alloc_size = filesize * 262144_int64

  dummy_char1 = ' allocation size:'
  write(*,'(A40,I12)') dummy_char1, alloc_size

  mib_written = filesize * nthreads
  dummy_char1 = ' MiB written:'
  write(*, '(A40,g0)') dummy_char1, mib_written
  mib_read = mib_written

  CALL OMP_SET_NUM_THREADS(nthreads)
  !$OMP PARALLEL default(shared) private(thread, filename, filenumber, values, tunit)
  do while (.true.)
     thread = omp_get_thread_num()
     write(filenumber,'(I0.10)') thread
     filename = trim(adjustl(directory_char)) // '/' // trim(adjustl(filenumber)) // '.temp'

     if (.not. allocated(values)) then
        allocate(values(alloc_size))
        call RANDOM_NUMBER(values)
     end if

     open(newunit=tunit, file=filename, status='replace', action='write', form='unformatted', access='stream')
     !$omp barrier
     !$omp master
     call system_clock(counti)
     !$omp end master
     !$omp barrier
     write(tunit) values
     close(unit=tunit)
     !$omp barrier
     !$omp master
     call system_clock(countf)

     telapsed_write = real(countf - counti, kind=real64)/real(count_rate, kind=real64)
     write_speed = mib_written/telapsed_write
     call execute_command_line ('echo 3 > /proc/sys/vm/drop_caches', wait=.true.)

     !$OMP END MASTER

     open(newunit=tunit, file=trim(adjustl(filename)), status='old', action='read', form='unformatted', access='stream')
     !$omp barrier
     !$omp master
     call system_clock(counti)
     !$omp end master
     !$omp barrier
     read(tunit) values
     close(unit=tunit)
     !$omp barrier
     !$omp master
     call system_clock(countf)

     telapsed_read = real(countf - counti, kind=real64)/real(count_rate, kind=real64)
     read_speed = mib_read/telapsed_read
     write(*,'(A29,2F10.3)') ' write / read speed (MiB/s): ', write_speed, read_speed
     !$OMP END MASTER

     call sleep(1)

  end do
  !$OMP END PARALLEL

END PROGRAM iometer

이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.

침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제

에서 수정
0

몇 마디 만하겠습니다

0리뷰
로그인참여 후 검토

관련 기사

TOP 리스트

  1. 1

    셀레늄의 모델 대화 상자에서 텍스트를 추출하는 방법은 무엇입니까?

  2. 2

    화살표와 테두리가있는 CSS 전용 툴팁

  3. 3

    Windows에서 Apache Kafka 오류-주 클래스 QuorumPeerMain을 찾거나로드 할 수 없습니다.

  4. 4

    Symfony 4 Validator-유효하지 않은 경우 자동 로그 아웃

  5. 5

    Zookeeper Cluster를 시작합니다. 오류 : 주 클래스 org.apache.zookeeper.server.quorum.QuorumPeerMain을 찾거나로드 할 수 없습니다.

  6. 6

    메모리 누수를 방지하기 위해 JDBC 드라이버가 강제로 등록 해제되었습니다

  7. 7

    PyCharm에서 프로젝트를 제거하는 방법은 무엇입니까?

  8. 8

    VSOMEIP-두 장치 (TCP / UDP) 간의 통신이 작동하지 않음

  9. 9

    Maven Groovy 및 Java + Lombok

  10. 10

    OpenCV로 여러 카메라 스트림을 캡처하는 방법은 무엇입니까?

  11. 11

    Gradle 프로젝트 종속성에서 META-INF를 어떻게 제외합니까?

  12. 12

    DbCommand.ExecuteNonQueryAsync ()> 0을 사용할 때 데이터베이스에서 쿼리가 실행되지 않습니다.

  13. 13

    wildfly에 배포하면 "15 초 후 잠금을 획득 할 수 없음"예외가 발생합니다.

  14. 14

    테스트의 JUnit 4에서 실행하지만 JUnit을 5하지 - 컴파일 깨끗하지만 0 테스트 실행

  15. 15

    Python을 사용하여 Windows 네트워크의 공유 폴더에 어떻게 액세스 할 수 있습니까?

  16. 16

    r의 24 시간 시계를 인식하도록 날짜 시간 열을 어떻게 분할합니까?

  17. 17

    오픈 JDK와 AdoptOpenJDK의 차이

  18. 18

    NVMe SSD의 GFortran 비 포맷 I / O 처리량

  19. 19

    div 사이의 여유 공간을 제거하는 방법

  20. 20

    스프링 부트 액추에이터 상태 엔드 포인트 + 동적 resilience4j 회로 차단기

  21. 21

    How do I make my own website able to be installed?

뜨겁다태그

보관