Docker + DeepSpeed + MultiGPU 사용 중 NCCL posix_fallocate failed: No space left on device 에러 대응하기

Posted on Thu, May 20, 2021 NLP MLDL Framework

어쩌다 이렇게...

학과 GPU서버 클러스터 액세스가 두 가지 방법으로 제공되는데,

이렇게 두 가지 방법을 쓸 수 있다.

보통의 경우 Root 권한을 사용가능한 Docker container 환경을 선호하는데, 해당 환경의 경우 이미 K8s등에서 띄워진 상태이기에 docker run ~~하는 config를 수정할 수가 없다.

한편 DeepSpeed(혹은 다른 Distributed trainer)에서 MultiGPU를 사용할 경우 GPU간 통신을 할 때 여러 방법을 사용하는데, 만약 GPU/Node간 InfiniBand같은 전용 통신시설이 설치되지 않았다면 PCI-E로 연결된 CPU, 즉 일반 RAM에 액세스를 해야한다.

하지만 Distributed trainer 사용시 보통 GPU 별로 process를 따로 띄워 사용하기 때문에, Gradient등을 GPU간 공유하기 위해서는 공유 메모리인 /dev/shm에 액세스를 해야 한다.

그런데, 이때 문제가 생긴다.

만약 Local(Host)에서 작업시 `/dev/shm`은 실제 램의 1x~2x 정도의 충분하고도 넘치는 양이 할당되기 때문에 이슈가 발생하지 않는다. (램이 8G라면 16G 정도가 기본값이다.)

하지만 Docker instance를 띄울 경우 기본값이 64MB로, 아주 작은 값이 할당된다.

NCCL의 경우 InfiniBand등 GPU간 전용 통신기기가 발견되지 않을 경우 Default Fallback으로 SHM을 사용하는데, 이로 인해 문제가 발생한다.

게다가.. 이건 deepspeed 혹은 pytorch단에서 에러가 발생하는 것이 아니라서, NCCL Debug를 켜두지 않으면 발견도 못하고 단순히 exit with code 1으로 알수 없게 프로그램이 종료되는 것으로밖엔 보이지 않는다.

따라서 해결책은 NCCL에서 SHM을 사용하지 않도록 만드는 것이다.

(그 외에... GPU P2P를 disable하거나 NCCL Buffer size를 조절하는 등 방법도 써봤지만 결국은 동일한 에러로 도달한다. 기본 Fallback이 SHM이기 때문.)

이렇게 SHM을 사용하지 않으면 GPU/worker간 통신이 모두 네트워크를 통하게 된다고 한다.

1Node일 경우 결국 로컬단에서 처리되기 때문에 속도 저하가 "그렇게" 크지는 않겠지만, MultiNode의 경우 네트워크 i/o 속도가 속도 저하의 가장 큰 원인이 될 수 있는 옵션일 것 같다.

(속도 저하가 얼마나 되는지는 큰 모델을 직접 Full로 학습해서 비교해보면 알 수 있을 듯 하다.)

Env & Command

실행 환경

Code Repo

Beomi/transformers-language-modeling

Fork from https://github.com/huggingface/transformers/tree/86d5fb0b360e68de46d40265e7c707fe68c8015b/examples/pytorch/language-modeling at 2021.05.17. Fine-tuning (or training from scratch) the library models for language modeling on a text dataset for GPT, GPT-2, ALBERT, BERT, DistilBERT, RoBERTa, XLNet... GPT and GPT-2 are trained or fine-tuned using a causal language modeling (CLM) loss while ALBERT, BERT, DistilBERT and RoBERTa are trained or fine-tuned using a masked language modeling (MLM) loss.

Bash command

export NCCL_IB_DISABLE=1
export BS=8
export NCCL_DEBUG=INFO # Debug 필수

deepspeed run_mlm.py \
--seed 42 \
--model_type bert \
--tokenizer_name beomi/KcELECTRA-base \
--train_file ./sampled_20190101_20200611_v2.txt \
--num_train_epochs 2 \
--per_device_train_batch_size $BS \
--per_device_eval_batch_size $BS \
--do_train \
--output_dir ./test-bert-zero2-multigpu \
--fp16 \
--logging_first_step \
--max_seq_length 300 \
--deepspeed ./ds_zero2_1gpu.json  \

Error Log

# [..생략..]
deepspeed_init

jupyter-beomi:9223:9776 [0] include/shm.h:28 NCCL WARN Call to posix_fallocate failed : No space left on device
jupyter-beomi:9223:9776 [0] NCCL INFO include/shm.h:41 -> 2

jupyter-beomi:9223:9776 [0] include/shm.h:48 NCCL WARN Error while creating shared memory segment nccl-shm-recv-d1133d3d8fb2856f-0-1-0 (size 9637888)
# [..생략..]

Solution

근본적 해결책

Docker container를 띄울 때 Shared Memory 부분을 늘려주거나, Host와 공유하도록 설정하자.

# Shared memory 늘리기
docker run --shm-size=256m --other-config....

docker config 수정이 불가능할 때

export BS=8
export NCCL_DEBUG=INFO
export NCCL_SHM_DISABLE=1 # <- 이 부분을 추가하면 된다!

deepspeed run_mlm.py \
--seed 42 \
--model_type bert \
--tokenizer_name beomi/KcELECTRA-base \
--train_file ./sampled_20190101_20200611_v2.txt \
--num_train_epochs 2 \
--per_device_train_batch_size $BS \
--per_device_eval_batch_size $BS \
--do_train \
--output_dir ./test-bert-zero2-multigpu \
--fp16 \
--logging_first_step \
--max_seq_length 300 \
--deepspeed ./ds_zero2_1gpu.json  \

Reference