5편-1. GPU 연결 구조

Gyullbb·2026년 8월 22일

LLM

목록 보기
7/7

LLM 서빙 최적화

LLM 서비스를 사용할 때 만족해야하는 조건들이 있다.

1)사용자 경험.
실제 LLM을 사용해보면 응답 속도에 따라 만족도가 달라진다. 특히 첫 토큰이 나오기까지 걸리는 시간이 길면 답답함을 느끼는 경우가 많다. 즉, 사용자 경험에는 지연시간이 중요하다.
이미 충분히 지연 시간이 축소되었다면 응답의 질이 얼마나 좋은지 즉, 같은 하드웨어·같은 지연 예산 안에서 얼마나 더 좋은 모델을 올릴 수 있느냐도 최적화의 요건이 된다.

2)비용. 모델을 학습시키는 비용은 선행 투자에 가깝지만, 추론은 트래픽이 늘어나는 만큼 끝없이 누적되는 비용이다. 에이전트형 워크플로우처럼 요청 하나에 LLM 호출이 여러 번 걸리는 구조가 늘면서 추론 비중은 계속 커지게 된다. 비용을 최소화 하는 것도 최적화의 요건이다.

3) 확장성과 배포 유연성. 트래픽은 평소엔 잔잔하다가 특정 시점에 몇 배씩 튈 수 있다. 최적화가 덜 된 시스템은 스파이크에서 지연 폭증이나 요청 실패로 다운 될 수 있다.

사용자 경험, 비용, 확장성을 만족하기 위해서는 LLM 서빙 최적화가 필수적이다.

LLM 서빙 최적화를 이야기 하기 위해서는 GPU로 얼마나 빠르게, 얼마나 많이 처리할 수 있는가라는 하드웨어에 대한 지식이 필수적이다.

최적화 기법(배치화, 양자화, KV 캐시 압축 같은 것들)을 알아보기 전에 GPU 스펙을 어떻게 읽어야 하는지, 그리고 GPU 여러 장을 묶어 쓸 때 그 GPU들이 서로 어떻게 연결되는지에 대해서 확인해본다.

GPU 스펙을 읽는 4가지 축

GPU를 서빙 관점에서 볼 때 알아야 하는 4가지 축이 있다.

  1. 연산 성능(FLOPS) — 초당 부동소수점 연산 횟수이다. 정밀도가 낮아질수록(FP32 → FP16 → FP8) 같은 칩에서 처리량이 대략 두 배씩 뛴다.
  2. 메모리 용량(VRAM) — 모델 가중치를 얹을 수 있는 공간으로 메모리 용량이 부족하면 애초에 모델을 로드할 수 없다.
  3. 메모리 대역폭 — GPU 메모리와 연산 유닛 사이 데이터 이동 속도로 연산 유닛이 아무리 빨라도 데이터를 제때 공급받지 못하면 놀게 된다.
  4. 인터커넥트 — GPU 간, 노드 간 데이터 전송 속도로 모델이 GPU 한 장에 다 안 들어가거나 여러 GPU가 협업해야 할 때 결정적이다.

넷 중 하나라도 부족하면 나머지가 아무리 좋아도 전체 처리량은 그 병목에 갇힌다. 앞의 셋은 스펙 시트에서 바로 확인되는 정보지만, 인터커넥트는 "PCIe인지 NVLink인지, 스위치가 있는지 없는지"에 따라 같은 GPU를 쓰고도 GPU 간 대역폭이 크게 벌어진다.
GPU가 어떻게 연결되는지 알아본다.

GPU 간 연결

GPU를 여러 장 묶을 때 실제로 쓰이는 연결 방식은 크게 두 계열로 나뉜다
범용 장치들을 연결하기 위해 만들어진 PCIe와, GPU 간 통신만을 위해 설계된 NVLink다.

PCIe

PCIe(PCI Express)는 CPU와 GPU·스토리지·NIC 같은 장치들을 연결하는 표준 범용 버스다.
물리적으로는 CPU의 루트 컴플렉스를 꼭짓점으로 하는 트리 구조를 이룬다.
스위치 하나에 GPU 여러 대가 매달리는데, 이 GPU들이 위(CPU)로 올라가는 통로는 하나뿐이고 그걸 나눠 쓴다.

이렇게 GPU 여러 장이 스위치 밑에 나란히 매달려 있고, 그 위로는 좁은 통로 하나만 있다.
스펙 시트에 적힌 "GPU당 128GB/s(Gen5 x16)"는 그 GPU가 스위치까지 뻗은 다운스트림 포트 하나의 최대치일 뿐이다. 여러 GPU가 동시에 통신하면 결국 스위치 위쪽의 업링크 용량을 나눠 가진다.
이렇게 되면 부족한 업링크 용량으로 인해 대역폭 문제가 생길 수 있다. 예시로 포트 수 × 포트당 대역폭을 계산해보자.

스위치 다운스트림 포트: 4개 × x16 슬롯

GPU 1장이 "낼 수 있는" 최대치: 128GB/s (Gen5 x16)

스위치 업스트림(스위치 → CPU) 링크: 128GB/s 1개
------------------------------------------------------

4개 GPU가 동시에 요구하는 대역폭 합 = 128GB/s × 4 = 512GB/s

실제 업링크 용량                    = 128GB/s

결론: 512GB/s > 128GB/s → 업링크가 감당 못 함 (오버섭스크립션, 블로킹 가능)

위 계산을 보면 실제 다운스트림 포트 대역폭의 합이 업스트림 링크 용량보다 훨씬 크게 설계되어 있어 업스트림 링크가 대역폭을 감당하지 못한다. — 이런 구조를 oversubscription이라 한다.

이는 PCIe는 애초에 GPU 전용 버스가 아니라 스토리지 컨트롤러, NIC, 각종 확장 카드까지 연결하는 범용 버스이기 때문에 나올 수 있는 설계이다.

PCIe TLP

이 물리 구조 위에서 실제로 오가는 패킷도 같은 논리를 따른다.
PCIe TLP(Transaction Layer Packet)는 계층이 나뉘어 있다.

Transaction Layer가 TLP 헤더+데이터를 만들면, Data Link Layer가 그 앞뒤로 Sequence Number(12bit)와 LCRC(4바이트, 오류 검출)를 덧붙이고, Physical Layer가 프레이밍 심볼을 씌워 내보낸다.
Start → Seq# → TLP Header → Data → (ECRC) → LCRC → End라는 꽤 긴 구조이다.

TLP 헤더 자체도 3DW(12바이트, 32비트 주소) 또는 4DW(16바이트, 64비트 주소)로, 그 안에 채워지는 필드가 많다.

  • Byte 0: Fmt(패킷 포맷: 헤더 길이·데이터 유무) + Type(Read/Write/Completion 등 요청 종류)
  • Byte 1: TC(Traffic Class, 8단계 QoS 우선순위)
  • Byte 2: TD(ECRC 존재 여부) + EP(Poisoned 데이터 표시) + Attr(순서 제어 속성) + AT(주소 타입) + Length 최상위 비트
  • Byte 3: Length 나머지 비트 (총 10비트, 최대 1024DW = 4KB까지 표현)
  • Byte 4-5: Requester ID (요청을 보낸 장치의 Bus:Device:Function 번호)
  • Byte 6: Tag (요청-응답을 매칭하기 위한 추적 ID)
  • Byte 7: Byte Enable (마지막·첫 DW에서 유효한 바이트만 표시하는 마스크)
  • Byte 8-11(혹은 8-15): Address

이 구조가 실제로 의미하는 건, PCIe TLP가 페이로드 크기와 무관하게 헤더(12~16B) + Seq#(12bit) + LCRC(4B)를 거의 고정으로 가져간다는 것이다. 4KB짜리 큰 데이터를 옮길 때는 이 오버헤드 비중이 미미하지만, 몇 바이트짜리 작은 요청 하나를 보낼 때도 이 헤더를 통째로 다 채워야 하니 오버헤드 비율이 커진다.

GPU 워크로드에서는 이것이 이론적인 비효율로 끝나지 않는다.
텐서 병렬화는 레이어 하나를 지날 때마다 GPU끼리 중간 결과(활성화 값, 어텐션 부분합)를 주고받아야 한다.
이런 메시지들은 대체로 몇 KB수준으로 작지만, 이런 작은 메시지가 레이어 수만큼 서빙 중이라면 토큰 하나가 나올 때마다 매번 PCIe는 고정 오버헤드를 다시 지불한다.
그 결과 스펙 시트에 적힌 대역폭까지 실제로 거의 도달하지 못하게 된다.
또한 헤더 파싱·라우팅 처리 시간이 메시지 수만큼 그대로 누적되면서, 레이어를 통과할 때마다 혹은 토큰을 하나씩 생성할 때마다 지연이 조금씩 쌓인다.
GPU 여러 장을 묶어 쓰는 순간, PCIe의 고정 오버헤드가 무시할 수 없는 비용이 된다.

PCIe 전체 배선

일반 PCIe 서버는 보통 CPU 소켓 2개짜리 듀얼소켓 서버이다.
GPU 절반은 CPU 0에, 나머지 절반은 CPU 1에 붙는다.
문제는 GPU끼리 통신할 전용 회선이 없기 때문에 GPU가 다른 GPU와 통신할 때는 "PCIe를 거쳐 CPU 쪽으로" 나가야 한다.
CPU 간 링크는 GPU 인터커넥트보다 대역폭이 한참 낮기 때문에, 병목이 될 수 있다.

NVLink는 PCIe 같은 범용 버스가 아니라, NVIDIA가 GPU와 GPU를 직접 잇기 위해 처음부터 새로 만든 전용 인터커넥트다.
각 GPU가 스위치(NVSwitch)까지 자기 전용 회선으로 직행하고, 스위치 내부는 모든 포트를 서로 잇는 방사형 구조이다.
PCIe와는 다르게 "업링크를 나눠 쓴다"는 개념 자체가 없다.

PCIe와 마찬가지로 포트 수 × 포트당 대역폭으로 계산해보자.
왜 이게 논블로킹(non-blocking)인지 바로 드러난다(H100 세대 NVSwitch 기준).

NVSwitch 포트 수: 18개 (18×18 완전연결 크로스바)

포트당 대역폭:    양방향 50GB/s (25GB/s × 2방향)

GPU 1장이 요구하는 NVLink 총 대역폭 = 900GB/s (18개 링크를 GPU 하나가 묶어서 사용)
---------------------------------------------------------

스위치 내부 총 스위칭 용량 = 18개 × 50GB/s = 900GB/s

900GB/s = 900GB/s → 포트 합계 용량이 GPU 요구 대역폭과 정확히 일치 (논블로킹 성립)

PCIe와 비교하였을 때 가장 큰 차이는 "스위치가 동시에 처리할 수 있는 총 용량"이 "포트에 매달린 장치들이 동시에 요구할 수 있는 대역폭 합"을 만족한다 는 것이다.

이렇게 설계된 이유는 GPU 워크로드의 특성때문이다. 텐서 병렬화나 all-reduce 같은 협업 연산은 여러 GPU가 거의 같은 순간에 서로 데이터를 주고받게 된다. NVSwitch는 처음부터 동시 요구를 기준으로 용량을 맞춰 설계되었다.

물리 구조만 다른 게 아니라, 그 위를 오가는 패킷 설계도 PCIe의 TLP와는 다르다.
NVLink는 TLP 같은 가변 길이 패킷이 아니라, 128비트(16바이트) 고정 크기의 Flit을 기본 전송 단위로 쓴다.
하나의 논리적 트랜잭션(요청/응답)은 1~18개의 Flit이 이어붙어 구성된다.
계층은 PCIe와 개념적으로 비슷하게 Transaction Layer(TL) → Data Link Layer(DL) → Physical Layer로 나뉘지만, 프레이밍·필드 폭이 훨씬 좁고 압축적이다.

  • CRC 25비트 — Flit 단위 오류 검출(PCIe의 LCRC와 같은 역할을 훨씬 잘게 쪼개서 수행)
  • Transaction Field 83비트 — 명령 종류, 대상 주소, 트랜잭션 ID 등(PCIe TLP의 Type+Address+Tag를 압축한 자리)
  • Data Link Field 20비트 — 흐름 제어(credit), 시퀀싱 정보(PCIe의 Sequence Number 역할)

여기서 진짜 차이는 자주 안 바뀌는 정보를 아예 기본 Flit 밖으로 뺐다는 점이다.
주소 범위가 넓어질 때만 붙는 AE(Address Extension) Flit, 바이트 단위 마스크가 필요할 때만 붙는 BE(Byte Enable) Flit처럼, PCIe라면 매 헤더마다 고정으로 소모했을 필드를 옵션으로 돌려서 평소엔 아예 보내지 않는다.

이 설계 차이가 왜 GPU 워크로드에 유리한지는 트랜잭션 크기를 생각해보면 알 수 있다. PCIe는 헤더(12~16B) + Seq#(12bit) + LCRC(4B)라는 프레이밍 오버헤드를 매 패킷마다 거의 고정으로 가져가는데, 이건 데이터가 4KB짜리 큰 전송이든 몇 바이트짜리 작은 동기화 신호든 똑같이 붙는다. 큰 전송에서는 이 오버헤드 비중이 작아지지만, 작고 잦은 메시지에서는 오버헤드 비중이 커진다.

NVLink는 아예 최소 단위 자체를 16바이트로 잡고 필요한 필드만 골라 붙이니, 이런 소규모·고빈도 트랜잭션에서 상대적으로 유리하다.
GPU들이 어텐션 연산 중 부분합을 주고받거나 파라미터 병렬화 과정에서 자잘한 동기화 신호를 쉴 새 없이 주고받는 상황에서는 이 구조가 유리하다.

NVLink는 NVIDIA 독점 기술이라 최신 세대(NVLink 4/5)의 세부 스펙은 대부분 비공개이고, 여기서 다룬 Flit 구조는 초기 세대(NVLink 1.0)에 공개된 자료를 바탕으로 한다

Form Factor: SXM

NVLink를 실제로 구현하려면 물리적 커넥터 자체 즉, 폼팩터(form factor) 가 필요하다.

PCIe 슬롯형 GPU는 일반 서버의 PCIe 슬롯에 그대로 꽂는 방식이다. 호환성이 좋고 저렴하지만, 슬롯 하나가 감당할 수 있는 핀 수에는 한계가 있다.

SXM은 이 제약을 없애기 위한 별도의 폼팩터다. 카드를 슬롯에 꽂는 게 아니라, GPU 모듈을 HGX 같은 전용 베이스보드에 소켓 형태로 직결한다.
이렇게 하면 GPU 한 장에서 18개 NVLink 링크를 전부 뽑아 NVSwitch로 보낼 수 있다.
SXM으로 인해 NVSwitch가 요구하는 핀 수와 전력을 물리적으로 감당할 수 있다.

SXM은 CPU-GPU 경로(PCIe)와 GPU-GPU 경로(NVLink)를 물리적으로 완전히 분리한다.
CPU는 가중치를 올리고 내리는 호스트 I/O만 담당하고, GPU끼리 주고받는 트래픽은 NVSwitch 패브릭을 따로 통째로 쓴다. 그 결과 GPU가 어느 소켓 밑에 있든 GPU-GPU 통신은 항상 같은 속도를 낸다.

모델 가중치를 로딩하는 트래픽(PCIe 경로)과 여러 GPU가 서로 협업하는 트래픽(NVLink 경로)이 서로 경합하지 않기 때문에, 로딩 중이라고 GPU 간 통신이 느려지거나, GPU끼리 바쁘게 통신 중이라고 호스트에서 데이터를 밀어 넣는 게 막히지 않는다.

노드 간 인터커넥트

한 노드에는 전력·공간 제약으로 GPU가 보통 8개까지만 들어간다. 그 이상을 묶으려면 노드를 넘어 InfiniBand(또는 RoCEv2) + GPUDirect RDMA를 써야 한다. GPUDirect RDMA는 노드 간 GPU끼리 CPU를 거치지 않고 서로의 메모리에 직접 접근하는 기술인데, 그래도 물리 네트워크 자체의 한계는 어쩔 수 없다

지금까지 다룬 인터커넥트 등급을 전부 한 표에 모아보면 격차가 정확히 얼마나 벌어지는지 드러난다(H100 기준).

NVSwitch (노드 내부)             : 900 GB/s
NVLink Bridge (노드 내부)        : 600 GB/s
PCIe (노드 내부)                 : 128 GB/s
InfiniBand (노드 간, NDR 400G)   :  50 GB/s

NVSwitch(900GB/s)와 InfiniBand(50GB/s)를 비교하면 900 ÷ 50 = 18배 차이가 난다. 노드 경계를 넘으면 대역폭이 이 정도로 줄어들기 때문에, 노드를 넘어서 GPU를 병렬화하려면 통신량 대비 연산량이 충분히 커야 손해를 보지 않는다. 대략 통신량 대비 18배 이상의 연산이 있어야 노드 내부와 비슷한 효율이 나온다는 계산이다.
이런 이유로 실무에서는 GPU 8장을 한 노드 안에 두고 최대한 해결하려는 경우가 많다. 노드 경계를 넘는 순간 GPU 간 통신 속도가 크게 떨어지기 때문에, 가능하면 노드 안에서 모델 인스턴스를 구성하고 트래픽이 늘면 그 단위를 그대로 복제해서 대응하는 방식을 쓴다.

여기까지는 인터커넥트 위주로 봤지만, GPU를 실제로 고를 때는 두 가지를 더 같이 봐야 한다.
하나는 연산 성능(FLOPS) — GPU가 초당 처리할 수 있는 연산 횟수로, 높을수록 같은 시간에 더 많은 행렬 연산을 처리할 수 있다.
다른 하나는 정밀도(precision) — 가중치와 연산에 쓰는 숫자 표현 방식이다. FP32/FP16/FP8처럼 표현에 쓰는 비트 수가 줄어들수록 같은 모델도 메모리를 덜 쓰고, 지원하는 GPU에서는 처리량도 늘어난다(FP8은 숫자 하나를 8비트로 표현하는 방식으로, FP16의 절반 크기다).

이 두 축까지 포함해서 실제 GPU 라인업에 대입해보면 스펙 표를 좀 더 쉽게 읽을 수 있다. 추론에 흔히 쓰이는 GPU들을 나란히 놓으면 대략 이런 표가 된다.

GPU메모리FP16/BF16 TFLOPS메모리 대역폭FP8 지원NVLink/NVSwitch
H200 SXM141GB~19794.8TB/sOO
H100 SXM80GB~19793.35TB/sOO
A100 SXM80GB~3121.935TB/sXO
L40S48GB~3620.864TB/sOX
A1024GB~1250.6TB/sXX

표만 보면 H200이 모든 축에서 좋아 보이지만 가격도 가장 높다. 결국 판단은 "이 모델이 이 GPU의 더 나은 스펙에서 실제로 이득을 보는가"를 중심으로 보면 된다.

  • 메모리: 가중치 + 예상 최대 KV 캐시가 그 GPU의 VRAM 안에 들어가는지 본다. 들어가지 않는다면 NVLink/NVSwitch 지원 여부가 필요해진다(여러 GPU에 분산 필요)

  • 정밀도: FP8을 지원하는 GPU라면 같은 모델을 절반 크기로 올릴 수 있어 메모리와 처리량 모두 여유가 생긴다. A100처럼 FP8을 지원하지 않으면 이 방법을 쓸 수 없다.

  • 연산·대역폭: decode 위주 워크로드는 메모리 대역폭 병목에 걸리는 경우가 많다. 그렇다면 FLOPS가 높은 GPU를 써도 그만큼 활용하지 못할 수 있어서, 대역폭과 메모리가 적당하면서 저렴한 GPU가 처리량 대비 비용 면에서 나을 수도 있다.

GPU 스펙을 확인할 때에는 "메모리가 들어가는가 → 정밀도로 더 줄일 수 있는가 → 병목이 연산인가 대역폭인가 → 여러 장이 필요한가" 순서로 확인해보는 게 GPU를 고르는 한 방법이 될 수 있다.

정리

여기까지 GPU 한 장의 스펙을 읽는 법(연산력·메모리·대역폭·인터커넥트·전력)과, 여러 GPU를 묶을 때 실제로 무슨 일이 일어나는지(PCIe의 오버섭스크립션, NVLink의 논블로킹 구조, 그 위를 오가는 패킷 설계, SXM 폼팩터, 노드 간 인터커넥트의 격차)를 봤다.
이 틀이 있으면 스펙 표에 적힌 숫자 하나하나가 왜 그 값인지 설명이 되고, 실제 GPU 라인업을 볼 때도 어떤 축을 먼저 확인해야 하는지 순서가 생긴다.

다음 글에서는 이 위에 LLM을 실제로 올려본다. 모델 로딩 단계에서 GPU 메모리가 무엇으로 채워지는지(가중치·KV 캐시·활성화), 그리고 모델 실행 단계에서 prefill과 decode가 왜 서로 다른 병목(연산 병목 vs 메모리 대역폭 병목)에 걸리는지를 루프라인 모델과 연산 강도로 확인해본다.

0개의 댓글