บทความนี้อธิบาย….. โดยหัวข้อต่อไปนี้ให้ข้อมูลสรุปเนื้อหาของบทความ เพื่อให้ผู้อ่านสามารถระบุส่วนที่ต้องการอ่านได้อย่างรวดเร็ว
บทความนี้อธิบายความหมายและการทำงานเบื้องต้นของ NCCL (NVIDIA Collective Communications Library) พื่อเพิ่มประสิทธิภาพการสื่อสารระหว่าง GPU พร้อมทั้งนำเสนอผลการทดลองของการ config parameter ต่างๆ เพื่อให้เข้าใจและเป็นแนวทางในการนำไปใช้งาน
ภาพรวมของ NCCL
NCCL (NVIDIA Collective Communications Library) เป็น library การสื่อสารแบบกลุ่มที่พัฒนาโดย NVIDIA สำหรับการทำ Parallel processing บน GPU โดยเฉพาะอย่างยิ่งในการ train deep learning model ที่ใช้ GPU หลายใบ NCCL ถูกออกแบบมาเพื่อเพิ่มประสิทธิภาพการสื่อสารระหว่าง GPU ในระบบที่มี GPU หลายใบ ทั้งภายใน single node และ multinode
การตั้งค่า NCCL parameters อย่างเหมาะสมมีความสำคัญในการเพิ่มประสิทธิภาพการทำงานของระบบ โดยเฉพาะใน cluster พารามิเตอร์ต่างๆ สามารถส่งผลกระทบต่อประสิทธิภาพการสื่อสารและการใช้ทรัพยากรของระบบ
การทดสอบหา NCCL parameters ที่เหมาะสม
ในการทดสอบ เราได้ทำการทดสอบ parameter 2 ตัวได้แก่ NCCL_SOCKET_NTHREADS
และ NCCL_NSOCKS_PERTHREAD
NCCL_SOCKET_NTHREADS
NCCL_SOCKET_NTHREADS
คือจำนวนการใช้งาน thread ของ CPU ที่จะใช้สำหรับเปิดการเชื่อมต่อระหว่าง node ผ่าน network socket
NCCL_NSOCKS_PERTHREAD
NCCL_NSOCKS_PERTHREAD
คือจำนวน network socket ที่ถูกใช้สำหรับแต่ละ thread ของ CPU
ตัวอย่างเช่น หากกำหนด NCCL_SOCKET_NTHREADS
เป็น 8 จะใช้งาน CPU 8 thread (สำหรับเครื่อง LANTA ที่ปิด Hyper-threading ไว้ การระบุจำนวน thread จะเทียบเท่ากับการใช้งาน CPU core) และเมื่อตั้งค่า NCCL_NSOCKS_PERTHREAD
เป็น 2 แต่ละ thread จะ 2 network socket ในการสื่อสาร
ดังนั้นจะใช้ทั้งสิ้น 16 network socket
ผลคูณของ NCCL_SOCKET_NTHREADS
และ NCCL_NSOCKS_PERTHREAD
จะต้องไม่เกิน 64.
โดย parameter 2 ตัวนี้ จะใช้ทรัพยากรส่วนหนึ่งของ CPU ในการสื่อสารกันระหว่าง GPU/node ซึ่งในการประมวลผล main task จะต้องใช้ทรัพยากรของ CPU เช่นกันจึงต้อง balance เพื่อหาค่า parameter ที่เหมาะสม
ผลลัพธ์ของการทดสอบ
สำหรับการทดสอบของเรา เราได้ทดลองการ train mode Llama2-13b ทดสอบโดยใช้ dataset alpaca(52k) ที่ 1 epoch และจำนวน 32 node และใช้ Deepspeed - ZeRO stage 3 ได้ผลลัพธ์ดังนี้
NCCL_SOCKET_NTHREADS | NCCL_NSOCKS_PERTHREAD | Testing Rounds | Average time (sec) | Standard Deviation |
8 | 2 | 4 | 307.91 | 3.21 |
16 | 2 | 4 | 309.88 | 7.19 |
8 | 4 | 4 | 311.61 | 2.29 |
16 | 4 | 4 | 313.36 | 0.98 |
4 | 4 | 4 | 316.64 | 3.39 |
8 | 8 | 4 | 321.70 | 3.98 |
4 | 8 | 1 | 321.78 | N/A |
2 | 2 | 4 | 323.78 | 2.90 |
2 | 8 | 1 | 326.43 | N/A |
2 | 16 | 1 | 348.85 | N/A |
1 | 1 | 4 | 373.15 | 6.39 |
หมายเหตุ: ในการทดสอบโดยใช้ model หรือ dataset อื่นอาจได้ผลลัพธ์ที่แตกต่างไป
ตัวอย่างการตั้งค่า NCCL parameters
ในตัวอย่างนี้ เป็นการตั้งค่า NCCL parameters โดยใช้ NCCL_SOCKET_NTHREADS=8
และ NCCL_NSOCKS_PERTHREAD=2
โดยสามารถดูรายละเอียดได้จากไฟล์ต่อไปนี้
ไฟล์ slurm_script.sh
#!/bin/bash #SBATCH -p gpu # Specify partition [Compute/Memory/GPU] #SBATCH -N 32 -c 64 # Specify number of nodes and processors per task #SBATCH --ntasks-per-node=1 # Specify number of tasks per node #SBATCH --gpus-per-node=4 # Specify total number of GPUs #SBATCH -t 1:00:00 # Specify maximum time limit (hour: minute: second) #SBATCH -A ltxxxxxx # Specify project name #SBATCH -J nccl # Specify job name #SBATCH -o logs/nccl-%j.out # Specify output file export NCCL_SOCKET_IFNAME=hsn # Specify Network Socket (High Speed Network) export NCCL_SOCKET_NTHREADS=8 export NCCL_NSOCKS_PERTHREAD=2 START=$(date) starttime=$(date +%s) export WANDB_MODE="offline" # sent to sub script export HOSTNAMES=$(scontrol show hostnames "$SLURM_JOB_NODELIST") export MASTER_ADDR=$(scontrol show hostnames "$SLURM_JOB_NODELIST" | head -n 1) export MASTER_PORT=12802 export COUNT_NODE=$(scontrol show hostnames "$SLURM_JOB_NODELIST" | wc -l) export SLURM_JOB_ID srun --output=${LOG_DIR}/node-%t.out sh smultinode.sh
ไฟล์ smultinode.sh
module restore module load Mamba module load Apptainer module load PrgEnv-gnu module load cpe-cuda/23.03 module load cudatoolkit/23.3_11.8 conda deactivate conda activate ./env echo -------ENVIRONMENT------- echo COUNT_NODE=$COUNT_NODE echo MASTER_ADDR= $MASTER_ADDR echo MASTER_PORT= $MASTER_PORT H=$(hostname) THEID=$(echo -e $HOSTNAMES | python -c "import sys;[sys.stdout.write(str(i)) for i,line in enumerate(next(sys.stdin).split(' ')) if line.strip() == '$H'.strip()]") echo THEID=$THEID echo SLURM_PROCID=$SLURM_PROCID echo ------------------------- export NCCL_TIMEOUT=3600000 export TORCH_NCCL_BLOCKING_WAIT=0 export TORCH_EXTENSIONS_DIR="./.cache" accelerate launch \ --num_processes $((4 * $COUNT_NODE)) \ --num_machines $COUNT_NODE \ --multi_gpu \ --mixed_precision bf16 \ --machine_rank $SLURM_PROCID \ --main_process_ip $MASTER_ADDR \ --main_process_port $MASTER_PORT \ --dynamo_backend inductor \ scripts/train.py
ข้อควรระวัง
ในการปรับ NCCL parameters ควรระวังการตั้งค่าที่สูงเกินไปอาจทำให้ระบบใช้ทรัพยากร CPU และ RAM มากเกินความจำเป็น อาจส่งผลให้เกิดการแย่งทรัพยากรกับกระบวนการอื่นๆ ใน main task เช่น การ load data, การปรับ weight, การ evaluate รวมถึงอาจเกิด overhead ในการจัดการ threads และ sockets ที่มากเกินไป