본문 바로가기
C.W.K.
Lesson 03 of 05 · published

외장 GPU에서 복사가 실제로 일어나는 자리

~15 min · gpu-uma, cuda, pcie, vram, offload, kv-cache

Level 0스펙 시트 훑는 사람
0 XP0/91 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete
"통합 메모리는 'PCIe를 건너뛰는' 게 아니야. PCIe가 토큰당 경로 위에 있는 경우 자체를 없애는 거지."

로드 시점: 건넘 한 번

외장 GPU가 있는 기계에서 모델의 삶은 호스트 메모리에서 시작해. CPU가 디스크에서 RAM으로 읽고, 그다음 cudaMemcpy 같은 호출로 호스트 버퍼에서 장치 버퍼로, PCIe 버스를 건너 카드 자체 메모리로, 딱 한 번 옮겨. 프레임워크는 전송이 버스 최고 속도로 돌 수 있게 호스트 페이지를 먼저 고정할 수도 있어. PCIe 5.0 x16이면 63 GB/s쯤이라서 14 GB 모델은 0.25초에, 60 GB 모델은 카드가 담을 수만 있다면 1초에 도착해. 잘 들어간 모델이 하는 복사는 이게 유일해. 그 뒤의 전부는 카드 안에서 일어나.

디코드: 들어가면 건넘 0회

생성되는 토큰마다 모든 가중치를 한 번 읽고(물리 트랙의 토큰당 바이트) 지금까지 쌓인 KV 캐시를 읽고, 새 캐시 몇 킬로바이트를 써. 외장 카드에선 그 트래픽 전부가 VRAM 속도의 VRAM 트래픽이야. RTX 5090이면 1,792 GB/s. 버스는 토큰 id 하나 들어가고 로짓이나 샘플된 토큰 하나 나오는 몇 바이트만 실어. CPU는 몰기만 하고 루프엔 없어. 외장 카드가 맥을 그냥 이기는 경우가 이거고, 이웃 퀘스트가 통합 메모리는 'PCIe를 완전히 건너뛴다'고 쓴 문장의 정직한 판은 이래. 모델이 VRAM에 들어가면 PCIe는 애초에 토큰당 경로에 없었고, 건너뛸 게 없다.

디코드: 안 들어가면 넘친 레이어마다 토큰당 건넘 한 번

모델이 안 들어가면 llama.cpp 같은 런타임은 몇 레이어를 GPU로 보내고 나머지를 CPU에 남길지 정하게 해. 호스트에 남은 레이어는 CPU가 호스트 메모리에서 계산하고, 그 활성값이 토큰마다 양방향으로 버스를 건너. 작은 전송이지만 동기 전송이고, 호스트 쪽 레이어는 행렬 처리량이 훨씬 낮은 프로세서에서 호스트 메모리 대역폭으로 돌아. 아니면 런타임이 가중치를 호스트 메모리에 두고 넘친 레이어의 가중치를 토큰마다 버스로 스트리밍할 수도 있어. 2번 트랙의 토큰당 63 GB/s 경로야. 어느 쪽이든 토큰당 비용에 이제 경계가 포함되고, 카드 자체 대역폭은 더는 중요한 숫자가 아니야. 코드 블록이 토큰 하나를 세 경우 모두 걸어 보내고 바이트마다 어디로 갔는지 찍어.

NVIDIA 자체 '통합 메모리'는 이게 아니야

CUDA엔 10년 전부터 통합 메모리라는 기능이 있어. cudaMallocManaged. CPU와 GPU에 주소 공간 하나를 주고 호스트 메모리와 VRAM 사이에서 요청에 따라 페이지를 옮겨. 같은 두 메모리, 같은 버스 위의 편의 기능이야. GPU의 페이지 폴트는 여전히 PCIe로 페이지를 끌어와. 애플 배치에 대한 NVIDIA의 진짜 답은 API가 아니라 하드웨어야. Grace Hopper와 Grace Blackwell은 CPU 메모리와 GPU 메모리를 900 GB/s NVLink-C2C 링크 뒤에 두고, DGX Spark의 GB10은 GPU한테 128 GB LPDDR5X를 "일관성 있는 통합 시스템 메모리"로 줘. 경쟁자 트랙이 이걸 진지하게 다뤄. 이 레슨의 요점은 더 좁아. 스펙 시트의 '통합 메모리'라는 말은 페이지 이전 API일 수도, 두 메모리 사이의 일관성 링크일 수도, 물리적 풀 하나일 수도 있고, 만들 복사가 없는 건 마지막 것뿐이야.

Code

token_path.py — 세 가지 메모리 배치에서 토큰마다 바이트가 어디로 가는지·python
#!/usr/bin/env python3
"""One decode step for a model with L layers of W bytes each, plus a KV cache
of K bytes, on three layouts. Prints bytes per memory tier and a time estimate.
Bandwidths GB/s: VRAM 1792 (RTX 5090), host DDR5 ~80 (dual-channel, typical),
PCIe 5.0 x16 63 per direction, unified M3 Ultra 635 (achieved, stream.py)."""

L, W = 40, 14.4e9 / 40          # a 14.4 GB model in 40 layers
K = 2.0e9                        # KV cache so far, 2 GB
ACT = 40e3                       # activations per layer crossing a boundary, ~40 KB


def layout(name, gpu_layers, streamed=False):
    on_card = gpu_layers * W
    spilled = (L - gpu_layers) * W
    t = on_card / 1792e9 + K / 1792e9                     # VRAM traffic
    crossings = 0
    if spilled:
        if streamed:                                       # weights stream across PCIe each token
            t += spilled / 63e9; crossings += (L - gpu_layers)
        else:                                              # layers computed on the host
            t += spilled / 80e9 + 2 * ACT / 63e9 * (L - gpu_layers); crossings += 2 * (L - gpu_layers)
    print(f"{name:34} card {on_card/1e9:5.1f} GB  host {spilled/1e9:5.1f} GB  crossings {crossings:3d}  ~{1/t:6.1f} tok/s")


layout("discrete, all 40 layers in VRAM", 40)
layout("discrete, 24 layers in VRAM (host)", 24)
layout("discrete, 24 layers, weights streamed", 24, streamed=True)
t = (14.4e9 + K) / 635e9
print(f"{'unified M3 Ultra (achieved 635 GB/s)':34} pool {14.4+2:5.1f} GB  host   0.0 GB  crossings   0  ~{1/t:6.1f} tok/s")
print("\nfits -> the card wins on bandwidth; spills -> the boundary is on every token.")

External links

Exercise

token_path.py를 돌리고 두 가지를 바꿔봐. 외장 카드에 96 GB(RTX PRO 6000)를 주고 모델을 60 GB로 올려. 호스트 계산 배치가 통합 메모리 맥의 속도 아래로 떨어지는 레이어 수를 찾고, 런타임의 --n-gpu-layers 플래그가 실제로 뭐랑 뭐 사이에서 고르는 건지 한 문장으로 써.
Hint
토큰마다 경계를 몇 번 건널지 고르는 거야. 호스트에 남긴 레이어 하나는 건넘 두 번과 느린 계산이고, 그 플래그는 카드 대역폭과 버스 대역폭 사이의 다이얼이야. 맥의 세일즈 포인트 전체는 512 GB 아래에선 그 다이얼이 존재하지 않는다는 거고.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인 — 댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.