2020년 2월 13일 목요일

IBM AC922 서버에서 CUDA-enabled HPL 수행하기


HPL (High Performance Linpack) 테스트는 수퍼컴 클러스터의 성능 측정에 널리 쓰이는 오픈소스 프로그램입니다.   GPU를 사용하여 HPL을 수행하기 위해서는 CUDA-enabled HPL이 필요한데, 그건 NVIDIA가 지적 재산권을 가진 프로그램이며 그건 오픈소스가 아닙니다.  WWW 상을 뒤져보면 CUDA-enabled HPL의 source code를 NVIDIA가 공개하기는 하는데, 그건 매우 오래된 GPU architecture인 Fermi 아키텍처의 GPU에 대한 것이라서 최신 GPU의 성능 측정에는 적절하지 않습니다.

아래에서는 NVIDIA의 협조를 받아 CUDA-enabled HPL의 executible binary file을 가지고 있다는 전제 하에 IBM AC922 서버 (POWER9 * 2, V100 SXM2 32GB GPU * 4) 1대로 CUDA-enabled HPL을 수행하는 과정만 제시합니다.

그 결과는 역시 confidential 정보라 공개하지 못하는 점 양해 부탁드립니다.

이 테스트 수행을 위해서는 서버에 먼저 CUDA 10.1이 설치되어 있어야 합니다.  또한 IBM의 XL Fortran, Spectrum MPI, ESSL 등의 library 등이 필요합니다.

[cecuser@p1235-met1 HPC]$ ls
ESSL_FOR_LINUX_ON_POWER_V6.2.0.tar.gz
hpl_cuda10.1.4gpus.tgz
IBM_SMPI_10.2_IP_GR_LINUX_PPC64LE.tgz
ibm_smpi_lic_s-10.02-p9-ppc64le.rpm
lsf10.1_lnx310-lib217-ppc64le.tar.Z
XL_FORTRAN_FOR_LINUX_V16.1.1_PRO.gz

먼저 XL Fortran을 설치합니다.

[cecuser@p1235-met1 HPC]$ mkdir xlf

[cecuser@p1235-met1 HPC]$ cd xlf

[cecuser@p1235-met1 xlf]$ tar -zxvf ../XL_FORTRAN_FOR_LINUX_V16.1.1_PRO.gz

[cecuser@p1235-met1 xlf]$ ./install
...
Press Enter to continue viewing the license agreement, or, Enter "1" to accept the agreement,
"2" to decline it or "99" to go back to the previous screen, "3" Print.
1
INFORMATIONAL: Unexpected CUDA Toolkit version detected '10.1' (9.2, 10.0 are supported), defaulting to __CUDA_API_VERSION=10000.  Re-configure with '-cudaVersion 9.2' to override.
Installation and configuration successful


이어서 Spectrum MPI를 설치합니다.  10.1이 아니라 10.2가 필요합니다.

[cecuser@p1235-met1 HPC]$ tar -zxf IBM_SMPI_10.2_IP_GR_LINUX_PPC64LE.tgz

[cecuser@p1235-met1 HPC]$ cd ibm_smpi-10.02.00.03-p9-ppc64le

[cecuser@p1235-met1 ibm_smpi-10.02.00.03-p9-ppc64le]$ sudo rpm -Uvh *.rpm ../ibm_smpi_lic_s-10.02-p9-ppc64le.rpm

[cecuser@p1235-met1 ibm_smpi-10.02.00.03-p9-ppc64le]$ su -

[root@p1235-met1 ~]# IBM_SPECTRUM_MPI_LICENSE_ACCEPT=yes /opt/ibm/spectrum_mpi/lap_se/bin/accept_spectrum_mpi_license.sh

[root@p1235-met1 ~]# exit

이어서 ESSL을 설치합니다.  이건 engineering용 library인데, GPU를 이용하도록 되어 있습니다.

[cecuser@p1235-met1 HPC]$ tar -zxvf ESSL_FOR_LINUX_ON_POWER_V6.2.0.tar.gz

[cecuser@p1235-met1 HPC]$ cd RHEL/RHEL7/

[cecuser@p1235-met1 RHEL7]$ su
Password:

[root@p1235-met1 RHEL7]# rpm -Uvh essl.license-6.2.0-0.ppc64le.rpm

[root@p1235-met1 RHEL7]# export IBM_ESSL_LICENSE_ACCEPT=yes

[root@p1235-met1 RHEL7]# /opt/ibmmath/essl/6.2/lap/accept_essl_license.sh

[root@p1235-met1 RHEL7]# rpm -Uvh essl.3264.rte-6.2.0-0.ppc64le.rpm essl.6464.rte-6.2.0-0.ppc64le.rpm essl.rte.common-6.2.0-0.ppc64le.rpm essl.man-6.2.0-0.ppc64le.rpm essl.3264.rtecuda-6.2.0-0.ppc64le.rpm essl.common-6.2.0-0.ppc64le.rpm essl.msg-6.2.0-0.ppc64le.rpm essl.rte-6.2.0-0.ppc64le.rpm


이제 CUDA-enabled HPL의 binary 및 script를 풀어냅니다.

[cecuser@p1235-met1 HPC]$ tar -zxvf hpl_cuda10.1.4gpus.tgz

[cecuser@p1235-met1 HPC]$ cd hpl

이 속에 들어있는 것은 간단합니다.  Binary 실행 파일인 xhpl과 함께, 그 수행에 필요한 HPL.dat, 기타 mpirun을 위한 script 입니다.

먼저 HPL.dat의 내용입니다.  Edit해야 하는 주요 내용은 아래 붉은 색으로 표시한 Ns (계산해야 하는 문제의 크기), NBs (한번에 어느 정도 크기로 문제를 풀 것인지 결정하는 block size), 그리고 mesh 구조를 결정하는 Ps와 Qs입니다. 

간단히 말하면 Ns는 가급적 GPU들의 메모리를 꽤 가득 채울 정도로 크게 하고, NBs는 적절한 크기를 trial & error 방식으로 찾아야 합니다.  가령 제가 해보니 아래와 같은 크기의 Ns면 32GB memory의 GPU 4장을 가득 채웁니다.  또한 256이나 768에 비해 512로 NBs를 두는 것이 가장 성능이 잘 나오는 것 같습니다.  Ps와 Qs는 서로 곱해서 GPU 갯수가 나오면 되는데, 가급적 서로 비슷하게, 그리고 가급적 Ps가 Qs보다 작게 설정하면 됩니다.

[cecuser@p1235-met1 hpl]$ cat HPL.dat
HPLinpack benchmark input file
Innovative Computing Laboratory, University of Tennessee
HPL.out      output file name (if any)
6            device out (6=stdout,7=stderr,file)
1            # of problems sizes (N)
128000       Ns
1          # of NBs
512        NBs
0            PMAP process mapping (0=Row-,1=Column-major)
1            # of process grids (P x Q)
2            Ps
2            Qs
16.0         threshold
1            # of panel fact
2            PFACTs (0=left, 1=Crout, 2=Right)
1            # of recursive stopping criterium
4            NBMINs (>= 1)
1            # of panels in recursion
2            NDIVs
1            # of recursive panel fact.
0            RFACTs (0=left, 1=Crout, 2=Right)
1            # of broadcast
3            BCASTs (0=1rg,1=1rM,2=2rg,3=2rM,4=Lng,5=LnM)
1            # of lookahead depth
0            DEPTHs (>=0)
1            SWAP (0=bin-exch,1=long,2=mix)
192          swapping threshold
1            L1 in (0=transposed,1=no-transposed) form
0            U  in (0=transposed,1=no-transposed) form
0            Equilibration (0=no,1=yes)
8            memory alignment in double (> 0)

여기서는 1대로 수행하니까 hosts 파일은 사실 필요가 없습니다만 아래와 같은 format으로 설정하면 됩니다.

[cecuser@p1235-met1 hpl]$ cat hosts
localhost  slots=4

아래는 mpirun을 수행하는 script입니다.  제가 쓴 환경처럼 infiniband가 없는 경우 "-pami_noib" 옵션을 써야 합니다.

[cecuser@p1235-met1 hpl]$ cat run_me_4_gpu_xlc_spectrum.sh
#!/bin/bash
export MPI_ROOT=/opt/ibm/spectrum_mpi
export MANPATH=$MPI_ROOT/share/man:$MANPATH
export PATH=/usr/local/cuda-10.1/bin:/opt/ibm/spectrum_mpi/bin:$PATH
export LD_LIBRARY_PATH=/opt/ibmmath/essl/6.2/lib64/:/opt/ibm/spectrum_mpi/lib:$LD_LIBRARY_PATH
sudo nvidia-smi -ac 877,1395
#echo always > /sys/kernel/mm/transparent_hugepage/enabled
TUNE="-x PAMI_IBV_DEVICE_NAME=mlx5_0:1 -x PAMI_IBV_DEVICE_NAME_1=mlx5_3:1 -x PAMI_ENABLE_STRIPING=0 -x PAMI_IBV_CQEDEPTH=4096 -x PAMI_IBV_ADAPTER_AFFINITY=1 -x PAMI_IBV_OPT_LATENCY=1 -x MLX5_SINGLE_THREADED=1 -x MLX5_CQE_SIZE=128 -x PAMI_IBV_ENABLE_DCT=1 -x PAMI_IBV_ENABLE_OOO_AR=1 -x PAMI_IBV_QP_SERVICE_LEVEL=8"
sudo ppc64_cpu --dscr=7
#mpirun -N 4 -npernode 4 --allow-run-as-root -x OMPI_MCA_common_pami_use_odp=0 -x PAMI_IBV_DEBUG_PRINT_DEVICES=1 -tag-output $TUNE --hostfile nodes -bind-to none ./run_linpack_6_gpu_xlc_spectrum_0726
mpirun -N 4 -npernode 4 --hostfile hosts -pami_noib -bind-to none ./run_linpack_4_gpu_xlc_spectrum.sh


그리고 아래가 실제 xhpl을 수행하는 script입니다.  위의 mpirun script를 수행하면 결국 아래의 script가 수행됩니다.  IBM의 Spectrum MPI에서는 내부적으로 OMPI_COMM_WORLD_LOCAL_RANK, PMIX 등의 환경 변수를 자동 생성하여 GPU를 할당하는데 사용합니다.  아래 script를 보면 case 문을 이용하여 CUDA_VISIBLE_DEVICES 환경 변수를 이용하여 GPU 1개씩마다 xhpl을 하나씩 수행합니다.


[cecuser@p1235-met1 hpl]$ cat run_linpack_4_gpu_xlc_spectrum.sh
#!/bin/bash
#location of HPL
HPL_DIR=`pwd`
# Number of CPU cores
# Total CPU cores / Total GPUs (not counting hyperthreading)
#CPU_CORES_PER_RANK=16
CPU_CORES_PER_RANK=8
export MPI_ROOT=/opt/ibm/spectrum_mpi
export OMP_NUM_THREADS=$CPU_CORES_PER_RANK
export MAX_H2D_MS=10
export MAX_D2H_MS=10
export RANKS_PER_SOCKET=2
export RANKS_PER_NODE=4
export NUM_WORK_BUF=4
export SCHUNK_SIZE=128
export GRID_STRIPE=4
export FACT_GEMM=1
export FACT_GEMM_MIN=128
export SORT_RANKS=0
export PRINT_SCALE=1.0
export TEST_SYSTEM_PARAMS=1
sudo rm -rf /dev/shm/sh_*
export LIBC_FATAL_STDERR_=1
#export PAMI_ENABLE_STRIPING=0
export CUDA_CACHE_PATH=/tmp
export OMP_NUM_THREADS=$CPU_CORES_PER_RANK
export CUDA_DEVICE_MAX_CONNECTIONS=8
export CUDA_COPY_SPLIT_THRESHOLD_MB=1
export GPU_DGEMM_SPLIT=1.0
export TRSM_CUTOFF=1000000
#export TRSM_CUTOFF=99000
export TEST_SYSTEM_PARAMS=1
export MONITOR_GPU=1
export GPU_TEMP_WARNING=70
export GPU_CLOCK_WARNING=1310
export GPU_POWER_WARNING=350
export GPU_PCIE_GEN_WARNING=3
export GPU_PCIE_WIDTH_WARNING=2
#export ICHUNK_SIZE=1536
export ICHUNK_SIZE=384
export CHUNK_SIZE=5120
APP=$HPL_DIR/xhpl
#lrank=$OMPI_COMM_WORLD_LOCAL_RANK
lrank=$(($PMIX_RANK%4))
nrank=$(($PMIX_RANK/4))
#crank=$(($nrank/89))
#neven=$(($crank%2))
neven=$(($nrank%2))
#neven=0
export CUDA_VISIBLE_DEVICES=$lrank
echo "RANK $PMIX_RANK on host $HOSTNAME PID $$ even: $neven"
if [ $neven -eq 0 ]
then
case ${lrank} in
[0])
#ldd $APP
sudo nvidia-smi -ac 877,1395 > /dev/null;
#export PAMI_IBV_DEVICE_NAME=mlx5_0:1;
#export OMPI_MCA_btl_openib_if_include=mlx5_0:1;
export CUDA_VISIBLE_DEVICES=0; numactl --physcpubind=0,4,8,12,16,20,24,28,32,36 --membind=0 $APP
  ;;
[1])
#export PAMI_IBV_DEVICE_NAME=mlx5_1:1;
#export OMPI_MCA_btl_openib_if_include=mlx5_1:1;
export CUDA_VISIBLE_DEVICES=1; numactl --physcpubind=40,44,48,52,56,60,64,68,72,76 --membind=0 $APP
  ;;
[2])
#export PAMI_IBV_DEVICE_NAME=mlx5_0:1;
#export OMPI_MCA_btl_openib_if_include=mlx5_0:1;
export CUDA_VISIBLE_DEVICES=2; numactl --physcpubind=80,84,88,92,96,100,104,108,112,116 --membind=8 $APP
  ;;
[3])
#export PAMI_IBV_DEVICE_NAME=mlx5_3:1;
#export OMPI_MCA_btl_openib_if_include=mlx5_3:1;
export CUDA_VISIBLE_DEVICES=3; numactl --physcpubind=120,124,128,132,136,140,144,148,152,156 --membind=8 $APP
  ;;
esac
exit
fi


이제 다음과 같이 run_me_4_gpu_xlc_spectrum.sh를 수행하시면 됩니다.  대략 10분 이내의 시간이 걸릴 것입니다.

[cecuser@p1235-met1 hpl]$ ./run_me_4_gpu_xlc_spectrum.sh


중간값을 빼면 결과적으로는 아래와 같은 결과물이 display 됩니다.   결과는 공개하지 못하는 점 다시 한번 양해 부탁드립니다.

...

================================================================================
T/V                N    NB     P     Q               Time                 Gflops
--------------------------------------------------------------------------------
WR03L2R4      128000   512     2     2             XXX              X.XXXe+04
--------------------------------------------------------------------------------
||Ax-b||_oo/(eps*(||A||_oo*||x||_oo+||b||_oo)*N)=        0.0005540 ...... PASSED
================================================================================




댓글 없음:

댓글 쓰기