gpumode · 강의 아카이브
《GPU Mode》 L067 2025 · MPI veteran High priority transcript · failed

NCCL and NVSHMEM — two GPU communication models, side by side

multi-GPU 시대의 communication 라이브러리 두 갈래 — NCCL 의 collective 추상과 NVSHMEM 의 PGAS put/get 추상. Jeff Hammond 가 깐 이 강의는 두 모델이 같은 하드웨어 위에서 무엇을 다르게 약속하는지를 정리한다. NCCL 의 allreduce 한 줄과 NVSHMEM 의 shmem_put 한 줄이 같은 NVLink 를 어떻게 다르게 사용하는지 — 그 차이가 곧 학습/추론 코드의 형태를 결정한다.

NCCL NVSHMEM PGAS collective ring tree GPU-initiated comm RDMA
J
Speaker
Jeff Hammond
NVIDIA · MPI/PGAS 베테랑 · ParRes Kernels 저자
강의 번호
L067
스피커
Jeff Hammond
학습 우선순위
High · 정독
코드
ParRes/Kernels
§ 01강의가 풀려는 문제· Two communication models, one hardware

“NCCL 만 있으면 되는 줄 알았는데, 왜 NVSHMEM 이 따로 있는가”

PyTorch 에서 dist.all_reduce 한 줄을 부르면 그게 NCCL 로 들어간다. 학습 코드의 99% 가 그 한 줄로 끝난다. 그런데 frontier inference / training 의 최근 코드 (DeepSeek, FlashInfer, NVSHMEM-based MoE) 는 NVSHMEM 을 직접 쓴다. 같은 NVLink, 같은 RDMA 위에서 — 왜 다른 인터페이스인가?

강의의 출발 질문 셋.

  1. NCCL 의 collective 추상이 왜 부족할 때가 있는가 — host-launched, batched, bulk 동기 모델 자체의 한계.
  2. NVSHMEM 의 PGAS 모델은 어떤 패턴을 추가로 표현 가능하게 하는가 — fine-grained, GPU-initiated, asynchronous put/get.
  3. 두 모델은 같이 쓰는가, 둘 중 하나만 쓰는가 — 답: 같이 쓴다. layer 가 다르다.

Jeff 의 입장은 “두 모델은 경쟁이 아니라 보완”이다. NCCL 은 큰 단위의 collective를 가장 빠르게, NVSHMEM 은 작은 단위의 비대칭 통신을 가장 자연스럽게. 이게 강의 전체를 끌고 가는 framing.

강의의 인지적 frame

두 라이브러리를 같은 추상 레이어로 보지 말 것 — NCCL 은 “모든 GPU 가 같은 데이터에 같은 연산을 한다” 의 collective 모델, NVSHMEM 은 “특정 GPU 의 특정 메모리 위치에 직접 쓴다” 의 one-sided 모델. 같은 trip 을 짤 때 “단체 관광버스” 와 “개별 우버” 의 차이.

“NCCL 은 batched, bulk synchronous. NVSHMEM 은 fine-grained, asynchronous. 둘 다 NVLink 위에서 돌지만 추상이 정반대다.”강의 §1 재구성 · Jeff Hammond
§ 02NCCL collective 의 추상· allreduce · broadcast · all-gather

“모두가 같이 부른다, 그리고 같이 끝난다” — bulk synchronous 의 약속

NCCL 의 핵심 단어는 collective. 모든 rank 가 같은 함수를 같은 인자로 부르고, 모두가 끝나야 다음 코드가 실행된다. ncclAllReduce, ncclBroadcast, ncclAllGather, ncclReduceScatter — 이 4-5개가 학습 코드의 99%.

collective 의 본질적 제약 두 가지.

  • 모든 rank 가 참여 — 한 rank 만 빠지면 deadlock. communicator 의 멤버십이 고정이라는 의미.
  • 모든 rank 가 같은 인자 — buffer 사이즈, dtype, op 가 일치해야 함. 한 rank 만 다른 사이즈를 부르면 정의되지 않은 동작.

이 제약이 곧 NCCL 의 강점이기도 하다 — 미리 알고 있으면 topology-aware 한 최적 알고리즘을 한 번 선택해서 같은 모양으로 반복할 수 있다. ring, tree, double binary tree — §05 에서 자세히.

PyTorch 위치

torch.distributed.all_reduce(tensor, op=ReduceOp.SUM) 한 줄이 들어가면 NCCL 의 ncclAllReduce 가 호출된다. backend='nccl' 으로 init 한 ProcessGroup 이 그대로 NCCL communicator. PyTorch 의 distributed API 의 추상이 곧 collective 모델 그 자체.

// NCCL 의 표준 사용 패턴 — host code
ncclComm_t comm;
ncclCommInitRank(&comm, nranks, id, rank);

// 각 rank 가 자기 GPU 에 sendbuff/recvbuff 준비
cudaMalloc(&sendbuff, size);
cudaMalloc(&recvbuff, size);

// 모든 rank 가 같이 부른다
ncclAllReduce(sendbuff, recvbuff,
              count, ncclFloat,
              ncclSum, comm, stream);

// stream 위에서 비동기로 실행됨 — 이후 sync 필요
cudaStreamSynchronize(stream);
FIG · 4 개 collective 의 데이터 흐름4 ranks 예시
AllReduce A B C A+B+C+D 모두 합 동일 Broadcast X · · X X X root → 모두 AllGather A B C ABC ABC ABC concat ReduceScatter A1B1 A2B2 A3B3 Σ1 Σ2 Σ3 분할된 합
AllReduce = ReduceScatter + AllGather 의 합성. 큰 메시지일 때 ring 알고리즘이 이 합성을 직접 활용해 latency 를 절반으로 줄인다 — §05 의 핵심.
§ 03NVSHMEM 의 PGAS 모델· Put · get · symmetric heap

“내가 너의 메모리에 직접 쓴다” — partitioned global address space

PGAS (Partitioned Global Address Space) 의 약속 — 각 GPU 의 메모리가 모두 같은 가상 주소 공간 안에 있다. 단, 물리적으로는 분산. 각 메모리 영역은 한 GPU 에 속한다 (partitioned). 다른 GPU 에 있는 변수에 접근하려면 shmem_get, 다른 GPU 에 쓰려면 shmem_put. 한쪽만 부르면 끝난다 — one-sided.

NVSHMEM 의 핵심 개념 셋.

  • symmetric heap — 모든 GPU 가 같은 사이즈의 heap 을 가지고, 같은 변수가 같은 offset 에 위치. nvshmem_malloc(size) 의 결과가 모두 같은 주소.
  • PE (Processing Element) — rank 의 NVSHMEM 명칭. nvshmem_my_pe() 가 자기 번호를, nvshmem_n_pes() 가 전체 수를 돌려줌.
  • one-sided primitivesnvshmem_put(dst, src, size, pe), nvshmem_get(dst, src, size, pe). 상대 PE 는 함수를 부르지 않아도 자기 메모리가 바뀐다.
왜 “symmetric”

같은 변수가 모든 GPU 의 같은 offset 에 있다는 약속이 있어야, “PE 3 의 변수 X 에 쓴다” 가 단순한 주소 계산으로 풀린다. 이게 PGAS 의 효율성의 출발점. MPI one-sided 와 같은 가족이지만 GPU-aware.

// NVSHMEM 의 표준 host code
nvshmem_init();
int me = nvshmem_my_pe();
int npes = nvshmem_n_pes();

// 모든 PE 가 같은 사이즈, 같은 offset
float* buf = (float*)
    nvshmem_malloc(N * sizeof(float));

// PE 0 이 PE 1 의 buf[0..N) 를 자기 src 로 덮어씀
if (me == 0) {
    nvshmem_float_put(buf, src, N, 1);
}
// PE 1 은 아무것도 안 부른다 — 자기 buf 가 바뀐다
nvshmem_barrier_all();

nvshmem_free(buf);
nvshmem_finalize();
// Device 에서 직접 — kernel 안에서 통신
__global__ void exchange(float* buf) {
    int tid = threadIdx.x;
    int me  = nvshmem_my_pe();
    int peer = (me + 1) % nvshmem_n_pes();
    // thread 단위 fine-grained put
    nvshmem_float_p(&buf[tid], src[tid], peer);
}

왜 GPU-initiated 가 결정적인가

NVSHMEM 의 진짜 강점은 device-side API. 위 두 번째 코드처럼 kernel 안에서 직접 nvshmem_float_p 를 부른다 — kernel 을 끝내고 host 로 나갔다가 NCCL collective 를 부르는 게 아니라, 같은 kernel 안에서 통신과 계산이 섞인다.

“NCCL 은 host 가 ‘이제 통신해라’ 라고 시킨다. NVSHMEM 은 GPU 가 자기 코드 흐름 안에서 직접 통신한다 — 이 차이가 latency 를 한 자릿수 바꾼다.”강의 §3 재구성
§ 04두 모델의 비교· When to choose what

같은 NVLink, 다른 추상 — 무엇이 무엇을 약속하는가

두 라이브러리를 한 표 위에 펼쳐놓는다. 같은 hardware 위에서 다른 약속을 한다는 점이 명확해야 한다.

FIG · NCCL vs NVSHMEM 의 12개 차원same hardware, different promises
차원NCCLNVSHMEM의미
communication 모델collectivePGAS / one-sided바라보는 시점
참여 방식모든 rank한 쪽만non-symmetric 가능
동기화자동명시적 fencememory ordering
호출 위치host (mostly)host + devicein-kernel comm
메시지 단위큰 bufferword/elementfine-grained
topology 인지자동 최적화사용자 책임algorithm 선택
latency (small msg)~10–30 µs~1–5 µsdevice-side 효과
bandwidth (large)peak 가까움peak 가까움큰 메시지는 둘 다
programming difficulty낮음높음trade-off
PyTorch 통합기본실험적DeepEP 같은 용도
대표 use caseDDP, ZeRO, TPMoE dispatch, EPcollective vs sparse
fault tolerance제한적제한적L070 PCCL 참고
"latency vs bandwidth" 의 측면이 §08 에서 본격적으로 다뤄진다. 작은 메시지에서는 NVSHMEM, 큰 batched 메시지에서는 NCCL 이 압도. 같은 GPU 시스템 위에서 두 도구를 같이 쓰는 게 자연스러운 결론.
NCCL 을 고르는 trigger
큰 batched, 같은 패턴 반복
  • 학습 루프의 gradient allreduce (DDP, FSDP)
  • 모델 weights 의 broadcast (init)
  • activation 의 all-gather / reduce-scatter (TP)
  • topology 가 복잡 (multi-node, IB + NVLink 혼합)
  • “PyTorch 가 이미 부르고 있는 것”
NVSHMEM 을 고르는 trigger
sparse, asymmetric, fine-grained
  • MoE dispatch / combine — 각 expert 가 다른 token 받음
  • DeepEP 같은 expert parallelism kernel
  • kernel 안에서 통신을 hide 하고 싶을 때
  • latency-bound 작은 메시지 (1KB 이하)
  • irregular pattern (bipartite scatter, halo exchange)
§ 05ring vs tree algorithms· NCCL internals

NCCL 이 같은 collective 를 두 가지 방식으로 — 메시지 사이즈에 따라 다르다

NCCL 의 black box 안에 들어가면 같은 allreducering 알고리즘tree 알고리즘 두 형태로 다르게 구현돼 있다. 작은 메시지에서는 tree (latency 우위), 큰 메시지에서는 ring (bandwidth 우위). NCCL 이 자동으로 고른다.

ring algorithm — bandwidth optimal

모든 GPU 가 ring 으로 연결됐다고 가정. 메시지를 N개 chunk 로 나눠 ring 위에서 N-1 step 을 반복. 각 step 에서 한 chunk 가 한 hop 이동. 결국 2(N-1) step으로 allreduce 가 끝남 (reduce-scatter + all-gather 합성).

강점 — 각 link 위 데이터 양이 (N-1)/N 으로 거의 1, 즉 bandwidth 거의 100% 사용. 약점 — step 수가 N 에 비례해 latency 누적.

tree algorithm — latency optimal

이진 트리 (또는 double binary tree) 형태로 GPU 들을 묶음. up-phase 에서 child 가 parent 로 reduce, root 에서 결과 집계 후 down-phase 에서 broadcast. step 수가 log N.

강점 — 작은 메시지에서 latency dominant 한 영역에서 ring 보다 빠름. 약점 — bandwidth 가 나무의 윗부분에서 병목 (root 가 단일 point).

FIG · ring vs tree 의 메시지 사이즈별 시간simple model
message size (log scale, KB → MB) time ring tree crossover tree 우세 ring 우세
crossover 는 ~1MB 부근 (확인 필요, GPU/NVLink 세대마다 다름). NCCL 은 NCCL_ALGO=Ring|Tree|Auto 환경변수로 강제 선택 가능 — 디버깅용.

double binary tree

현대 NCCL 의 기본 — 두 개의 보완적 binary tree 를 동시에 돌려 tree 의 bandwidth 약점을 보강한다. 각 tree 가 서로 다른 link 만 사용하도록 짜여 있어서 NVLink 의 양방향 대역폭을 모두 활용. 이게 NCCL 2.x 가 “fastest collective library” 의 자리에 머물게 한 핵심 알고리즘.

§ 06GPU-initiated 통신· Device-side calls

kernel 이 끝나기 전에 통신을 시작한다 — overlap 의 진짜 의미

NCCL 은 host 가 launch 하는 collective. 그 결과 — kernel 이 끝나야 통신이 시작된다 (또는 별도 stream 위에 띄워야 함). NVSHMEM 은 kernel 안에서 직접 put/get. 한 thread block 이 작업을 끝내자마자 그 결과를 다른 GPU 로 send 시작.

FIG · NCCL host-launch vs NVSHMEM device-initiated같은 일, 다른 timeline
NCCL pattern (host-launched) CPU launch K1 launch allreduce launch K2 GPU kernel 1 (compute) NCCL allreduce kernel 2 → compute 와 통신이 sequential NVSHMEM pattern (device-initiated) CPU launch fused K GPU compute put compute put
위 timeline 은 단순화. NCCL 도 별도 stream 으로 overlap 시킬 수 있지만, 같은 kernel 안에서의 fine-grained interleaving 은 NVSHMEM 의 device API 만 가능.

실전 예 — DeepEP / DeepSeek 의 expert parallelism dispatch. token 마다 어느 expert 로 갈지가 다르다. 이걸 NCCL 의 모든 token batched all-to-all 로 보내면 padding 과 sync 비용이 크다. NVSHMEM 의 per-token put 으로 보내면 정확히 필요한 만큼만, kernel 안에서 직접.

하드웨어 전제

device-initiated 통신은 GPUDirect RDMA (NIC ↔ GPU 메모리 직접 access) 와 IB Verbs / NVLink 의 GPU-side 트리거가 필요. 모든 cluster 에서 되는 건 아니다 — H100 + ConnectX-7 + IB switch 같은 환경에서 검증됨.

§ 07fault tolerance 와 retry· Resilience patterns

두 라이브러리 모두 “하나 죽으면 모두 죽는다” — 그 전제가 흔들리고 있다

NCCL 과 NVSHMEM 모두 tightly coupled 모델. communicator 안의 한 GPU/노드가 죽으면 collective 전체가 멈추고, 보통 process group 을 다시 만들어야 한다. 큰 학습 클러스터 (10k GPU) 에서는 이게 사실상 항상 일어난다.

현재의 핸들링 패턴 — checkpoint + restart. 정기적으로 체크포인트를 저장하고, NCCL communicator 가 깨지면 모든 rank 가 재시작해서 마지막 체크포인트부터 다시.

  • 장점 — 단순하다. 추가 인프라 안 필요.
  • 단점 — 재시작 비용이 크다. 10k GPU 클러스터에서 한 GPU 가 죽으면 모두 재시작.

이걸 깬 새 라이브러리가 L070 PCCL 의 시도 — collective 안에서 일부 rank 의 dropout 을 견디는 fault-tolerant collective. NCCL 자체는 NCCL 3.x 에서 partial recovery 를 검토중 (확인 필요).

FIG · failure modes현재 NCCL/NVSHMEM
실패 종류NCCLNVSHMEM
단일 GPU 죽음전체 hang전체 hangrestart 필요
노드 죽음restartrestart동일
network flaptimeouttimeoutenv timeout
stragglers대기대기slow rank 가 느림
elastic shrink미지원미지원PCCL 가 시도
“NCCL 은 ‘모두가 살아있다’ 를 가정한다. 1만 GPU 클러스터에서 그 가정은 매시간 깨진다 — fault tolerance 는 더 이상 옵션이 아니다.”학습 노트 · §7
§ 08latency vs bandwidth· Message-size regimes

같은 라이브러리 안에서도 메시지 사이즈에 따라 다른 알고리즘을 선택해야 하는 이유

communication 의 cost 모델 — T(n) = α + β·n. α 는 latency floor, β 는 reciprocal bandwidth, n 은 메시지 사이즈. 이 단순한 모델 위에서 알고리즘 선택의 모든 기준이 나온다.

FIG · 메시지 사이즈 regimeα-β model
regime크기 (대략)지배 비용최선 도구
tiny (control flag)≤ 16 Bα dominantNVSHMEM put
small (token feature)~ 4 KBα dominantNVSHMEM
medium (layer slice)~ 1 MBcrossoverNCCL tree
large (gradient bucket)~ 100 MBβ dominantNCCL ring
xlarge (whole model)~ 10 GBβ dominantNCCL ring + chunking
PyTorch DDP 의 gradient bucketing 이 정확히 이 regime 위에서 설계됐다 — 작은 gradient 들을 합쳐 ~25MB 단위로 묶어 NCCL ring allreduce 의 sweet spot 을 만든다.

그리고 — “latency 와 bandwidth 는 같은 hardware 위에서도 다른 측정치”. NVLink 의 peer bandwidth 는 ~600 GB/s 인데 latency 는 ~1 µs 영역. 같은 NVLink 가 어떤 메시지에 대해서는 빠르고, 어떤 메시지에 대해서는 어쩔 수 없이 늦다 — 사이즈가 작으면 α 가 지배.

측정 시점

NCCL bench (nccl-tests) 를 돌릴 때 꼭 메시지 사이즈를 sweep 한다. ./all_reduce_perf -b 8 -e 1G -f 2 처럼. 8B 부터 1GB 까지 두 배씩 — α 와 β 가 그래프에서 직접 분리된다.

§ 09NCCL 3.x 의 미래· Where it heads

frontier roadmap — symmetric memory, fault tolerance, GPU-initiated

강의의 끝부분은 “NCCL 이 어디로 가는가”. 흥미롭게도 NCCL 의 진화 방향이 NVSHMEM 의 기능을 흡수하는 방향이다 — 같은 라이브러리 안에서 두 모델을 모두 제공.

관전 포인트

NCCL 과 NVSHMEM 의 경계가 점점 흐려지고 있다. 사용자 입장에서는 “하나의 라이브러리에 두 모드가 있다” 가 자연스러운 미래. 이 미래의 사용자는 “collective 를 부를지, put 을 부를지” 를 알고리즘 단위로 고른다 — 라이브러리 단위가 아니라.

“통신 라이브러리의 미래는 추상이 아니라 메시지 사이즈 단위로 알고리즘을 자동 선택하는 것 — 사용자는 의도만 적는다.”강의 §9 재구성
§ 10기억할 메모와 코드 자료· Key takeaways · repo

다시 열었을 때 5분 안에 잡혀야 할 것

collective 모델
모든 rank 가 같은 함수, 같은 인자, 같이 끝남. NCCL 의 약속.
PGAS / one-sided
한 쪽만 부르면 끝. symmetric heap. NVSHMEM 의 약속.
device-initiated
kernel 안에서 직접 통신. NVSHMEM 의 진짜 차별점.
α + β·n
latency floor + reciprocal bandwidth × size. 모든 알고리즘 선택의 기준.
ring vs tree
tree 는 latency 우위, ring 은 bandwidth 우위. NCCL 이 자동 선택.
double binary tree
tree 의 BW 약점을 두 tree 로 보강. NCCL 2.x 의 기본 알고리즘.
NCCL_ALGO 환경변수
디버깅용 강제 선택. Ring | Tree | Auto.
fault tolerance 격차
두 라이브러리 모두 tightly coupled. 1만 GPU 시대에는 한계.

손에 새기기 — 실습 시퀀스

  1. nccl-tests bandwidth sweep./all_reduce_perf -b 8 -e 1G -f 2 -g 4. 8B 부터 1GB 까지 sweep. α 와 β 를 그래프로 분리.
  2. NCCL_DEBUG=INFO — 학습 코드 한 번 돌려 NCCL 이 ring vs tree 중 무엇을 골랐는지 직접 확인. NCCL INFO Channel/Tree 라인.
  3. NVSHMEM hello world — ParRes/Kernels 의 stencil 예제. 두 PE 사이의 halo exchange 를 NVSHMEM put 으로 직접 구현.
  4. device-initiated put — kernel 안에서 nvshmem_float_p 를 부르는 커널 작성. host-launched NCCL allreduce 와 latency 비교.
  5. PyTorch SymmetricMemory — torch 2.4+ 의 새 API. NCCL symmetric memory 위에서 작은 메시지 communication 패턴 직접 작성.
  6. NCCL_ALGO 강제 — 같은 allreduce 를 NCCL_ALGO=RingNCCL_ALGO=Tree 로 두 번 돌려 메시지 사이즈별 차이 측정. crossover 직접 찾기.
§ 11다른 강의로 이어지는 길· Connections

communication / scaling / inference 의 가족 트리

이 강의의 두 도구가 다른 강의들에서 어떻게 다시 등장하는지 — communication 가족의 핵심 노드.

§ 12열린 질문· Open questions

이 노트가 의도적으로 비워둔 자리들

검증 메모

본문의 latency 수치 (NCCL ~10–30 µs, NVSHMEM ~1–5 µs) 는 일반적 도메인 지식 범위에서 가져온 추정치. 정확한 값은 특정 GPU 세대 + 특정 NIC 조합에서의 직접 측정 필요. 강의 영상이 다시 transcript 가능해지면 Jeff 가 인용한 정확한 숫자로 교체 권장.

← Lecture 066 Game Arena — Lanxiang Hu Lecture 068 → Landscape of GPU-centric communication — Didem Unat