Closed HyunChaeJung closed 1 year ago
*CPU: Intel Xeon Silver 4216 16C 2.10GHz 2대** 코어 수: 16 개 (총 32 개) / 스레드 수: 32 개 (총 64 개) 프로세서 기본 주파수(기본 클럭): 2.10 GHz / 프로세서 최대 주파수(최대 클럭): 3.20 GHz 인텔(소켓3647) / PCIe3.0
GPU: NVIDIA Quadro RTX6000 CUDA cores: 4,608 / NVIDIA tensor cores: 576 / NVIDIA RT cores: 72 GPU memory: 24 GB GDDR6 with ECC / Bandwidth: 624 GB/sec 16.3 TFLOPS FP32 Performance / 32.6 TFLOPS FP16 Performance System Interface: PCI Express 3.0 * 16
~/PSyclone-2.0.0/config/psyclone.cfg
# Setting specific to the Nemo API
# ================================
[nemo]
# The valid types of loop and associated loop variable and bounds:
mapping-lon = var: ji, start: 1, stop: jpi
mapping-lat = var: jj, start: 1, stop: jpj
mapping-levels = var: jk, start: 1, stop: jpk
mapping-tracers = var: jt, start: 1, stop:
mapping-unknown = var: , start: 1, stop:
# Used for converting implicit loops to explicit loops
index-order = lon, lat, levels, tracers
[check openacc version]
PROGRAM test
USE openacc
print*, _OPENACC
END PROGRAM
$ nvfortran -acc -o a.out check_acc_ver.F90
$ ./a.out
$ 201711
'201711' => version 2.6
The OpenACC Application Program Interface version 2.6 (파일 위치: \Nextcloud\2021 차세대 전산과학\05 프로젝트수행\1-1 autocoding\reference\openacc_compile_method(options)\OpenACC-ver.2.6-Application_Programming_Interface_201711)
cf)
$ gfortran -fopenacc -o a.out check_acc_ver.F90
$ ./a.out
$ 201306
'201306' => version 2.0
$ echo | cpp -fopenmp -dM | grep -i open
$ #define _OPENMP 201511
'201511' => version 4.5
+) 460.27.04+ (CUDA 11.2) CUDA toolkit(library) 11.2 NVIDIA HPC SDK 22.2 -> 여기서 제공해 주는 nvc, nvfortran이 OpenACC 지원
PROGRAM nemolite2d
모듈 선언
변수 선언
CALL setup
CALL ...
...
CONTAINS
SUBROUTINE setup
...
END SUBROUTINE setup
SUBROUTINE etc...
...
END SUBROUTINE ...
END PROGRAM nemolite2d
OpenMP 지시문을 삽입할 때와 동일한 방법(아래)으로 진행함.
PROGRAM
, END PROGRAM
을 MODULE
, END MODULE
로 변경CONTAINS
이전에는 모듈 선언
, 변수 선언
만 존재하도록 다른 부분 삭제MODULE
, END MODULE
을 PROGRAM
, END PROGRAM
으로 다시 변경하고, 2번 과정에서 삭제한 부분을 다시 추가(4번) 코드 생성 시(PSY.gen
) IF ~ CYCLE
문을 인지하지 못하여 오류 발생
!subroutine momentum
DO jj = 1, jpj
DO ji = 1, jpi-1
IF(pt(ji,jj) + pt(ji+1,jj) <= 0) CYCLE !jump over non-computatinal domain
IF(pt(ji,jj) <= 0 .OR. pt(ji+1,jj) <= 0) CYCLE !jump over boundary u
END DO
END DO
nemolite2D serial 코드에서 original 코드와 IF ~ CYCLE
문을 모두 삭제한 코드의 결과 값이 차이가 없어 (go2d_00000.dat
(++22.08.29))
IF ~ CYCLE
문을 모두 삭제 후 4번을 진행함
++) 22.06.15
< psyclone NEMO API를 활용하기에 앞서 CYCLE
문을 주석 처리 할 경우 >
~/fparser/common/readfortran.py
) 주석문을 무시하고 읽기 때문에(ignore_comments=True
) 주석문이 삭제된 코드에서 구문 분석이 진행됨. 따라서 삭제된 주석문을 다시 원래 위치에 살리는 것은 어려움 ignore_comments=False
로 설정한 후 진행해 보았으나 psyclone이 ignore_comments=True
기반으로 설계되었기 때문에 CYCLE
문에 대한 고민이 필요한 것으로 보임# ~/fparser/common/readfortran.py
class FortranFileReader(FortranReaderBase):
...
def __init__(self, file_candidate, ..., ignore_comments=True):
...
++)22.08.30
IF ~ CYCLE
문이 존재하는 서브루틴은 momentum
, bc
, next
임.
IF ~ CYCLE
문을 통해 ssh
및 u-
, v-velocity
를 계산하는 영역이 정해짐IF ~ CYCLE
문을 주석 처리 할 경우 next
서브루틴에서 sshn_v
의 계산 값이 달라짐IF ~ CYCLE
문을 모두 삭제하고 모델을 수행할 경우 모델 결과 값이 다르게 계산될 수 있어 주의해야 함IF ~ CYCLE
문을 수동으로 제 위치에 삽입하여 모델을 수행하였음IF ~ CYCLE
문이 쓰이는 코드
~./OPA_SRC/BDY/bdyice_lim.F90
~./OPA_SRC/OBS/obs_read_seaice.F90
~./OPA_SRC/OBS/obs_read_sst.F90
~./OPA_SRC/OBS/obssla_io.h90
~./OPA_SRC/OBS/obs_read_vel.F90
~./OPA_SRC/OBS/obs_grid.F90
~./OPA_SRC/OBS/obs_read_prof.F90
~./OPA_SRC/OBS/obs_read_sla.F90
~./OPA_SRC/OBS/obsinter_h2d.h90
~./OPA_SRC/OBS/obs_prep.F90
~./OPA_SRC/ICB/icblbc.F90
~./OPA_SRC/nemogcm.F90
OpenACC 지시문 삽입 전
SUBROUTINE continuity
!kernel continuity
DO jj = 1, jpj
DO ji = 1, jpi
rtmp1 = (sshn_u(ji ,jj ) + hu(ji ,jj )) * un(ji ,jj )
rtmp2 = (sshn_u(ji-1,jj ) + hu(ji-1,jj )) * un(ji-1,jj )
rtmp3 = (sshn_v(ji ,jj ) + hv(ji ,jj )) * vn(ji ,jj )
rtmp4 = (sshn_v(ji ,jj-1) + hv(ji ,jj-1)) * vn(ji ,jj-1)
ssha(ji,jj) = sshn(ji,jj) + (rtmp2 - rtmp1 + rtmp4 - rtmp3) * rdt / e12t(ji,jj)
END DO
END DO
!end kernel continuity
END SUBROUTINE continuity
OpenACC 지시문 삽입 후
subroutine continuity()
!$acc data copyin(e12t,hu,hv,sshn,sshn_u,sshn_v,un,vn) copyout(ssha)
!$acc kernels
do jj = 1, jpj, 1
do ji = 1, jpi, 1
rtmp1 = (sshn_u(ji,jj) + hu(ji,jj)) * un(ji,jj)
rtmp2 = (sshn_u(ji - 1,jj) + hu(ji - 1,jj)) * un(ji - 1,jj)
rtmp3 = (sshn_v(ji,jj) + hv(ji,jj)) * vn(ji,jj)
rtmp4 = (sshn_v(ji,jj - 1) + hv(ji,jj - 1)) * vn(ji,jj - 1)
ssha(ji,jj) = sshn(ji,jj) + (rtmp2 - rtmp1 + rtmp4 - rtmp3) * rdt / e12t(ji,jj)
enddo
enddo
!$acc end kernels
!$acc end data
end subroutine continuity
OMP_NUM_THREADS=8
) (gnu compiler 사용)
PGI compiler
Makefile.include.pgi
(Original ver., OpenMP ver.)F90 = pgf90
NETCDF_INC = /usr/local/netcdf/netcdf-fortran-4.4.4/PGI/include NETCDF_LIB = /usr/local/netcdf/netcdf-fortran-4.4.4/PGI/lib
F90FLAGS += -O3 LDFLAGS += -O3 OMPFLAGS = -mp=multicore -Minfo=mp
```~/.bash_profile``` : ```OMP_NUM_THREADS=4```
* ```Makefile.include.pgacc``` (OpenACC ver.)
``` shell
F90 = pgf90
NETCDF_INC = /usr/local/netcdf/netcdf-fortran-4.4.4/PGI/include
NETCDF_LIB = /usr/local/netcdf/netcdf-fortran-4.4.4/PGI/lib
F90FLAGS += -O3 -acc -ta=nvidia -Minfo=accel
LDFLAGS += -O3 -acc -ta=nvidia -Mcuda
Nsight System
- Serial ver.
$ nsys profile -o nemolite2d_serial_profile ./nemolite2d.exe
- OpenMP ver.
$ nsys profile -t openmp -o nemolite2d_openmp_profile ./nemolite2d.exe
Nsight System은 OpenMP 5.0 이상의 버전을 지원하는데, 클러스터에 설치된 NVIDIA HPC SDK 22.2는 OpenMP 5.0을 지원함에도 불구하고 OpenMP의 버전은 4.5인 것으로 확인됨.- OpenACC ver.
$ nsys profile -t openacc,cuda -o nemolite2d_acc_profile ./nemolite2d.exe
- Profile Command Switch Options
재밌는 분석 결과네요. 모든 루프에 openACC 적용하기 보단, CPU<->GPU 데이터 통신으로 인한 속도 저하(단점)와 병렬로 인한 속도 개선(장점)간의 밸런스가 중요하다는 이야기죠? 이 내용 잘 정리해서 분석하면, 앞으로 진행할 nemo+openACC 적용 전략 세울 수 있겠어요. 해당 내용 reference들(이전에 분석한 gnemo, nemo+openacc 보고서 내용) 참조해서 분석해주세요. 학회나 과제 보고 발표때 들어가면 좋을 내용들 같습니다.
아 그리고 질문이 한가지 더 있습니다. openMP와 openACC는 LAT 루프에만 저렇게 넣어도 내부에 위치한 LON 루프에 병렬이 적용되지 않는 건가요?
OpenACC를 코드에 적용할 때 어떤 루프에, 어떤 옵션을 적용하면 효율적일지 고민이 필요할 것 같습니다. lat 루프에 병렬화 지시문을 삽입하면 내부에 위치한 lon 루프는 직렬로 계산되는 것으로 알고 있습니다. (예: lat 루프에 0~3번 thread 중 2번 thread가 할당되면 내부 lon 루프는 2번 thread로만 계산)
!$acc kernels
지시문을 사용하면 수행 시간이 무한으로 길어지는 문제가 있어 !$acc parallel
및 !$acc loop
지시문을 사용함
kernels
: 병렬 여부를 컴파일러가 스스로 판단parallel
: 병렬 여부를 사용자(프로그래머)가 판단
[MPI+OpenACC]
- NEMO + OpenACC 테스트: 1node 36 core mpi vs. 1node 1core + GPU
- CPU <-> GPU 메모리 이동시 부동소수점 보존 이슈
--- openacc 부동 소수점 보존됨.
- 격자 dependency 이슈
--- 격자 dependency 있을 때, openmp, openacc 적용 유의해야하며, 코드를 변경하는 방식으로 해결/적용 가능