GPU에 대한 기본 팩트들
(damek.github.io)- GPU는 연산 속도가 메모리 접근 속도보다 월등히 빨라서, 메모리 계층 구조가 성능의 병목을 일으킴
- 연산 집약도(Arithmetic Intensity, AI) 에 따라 연산이 메모리 바운드, 계산 바운드 상태로 구분되며, A100 GPU의 임계점은 약 13 FLOPs/Byte임
- 성능 최적화 주요 전략으로 연산 합치기(Fusion)와 타일링(Tiling)이 있음; Fusion은 불필요한 메모리 왕복을 줄이고, Tiling은 데이터 재사용을 극대화함
- 동기화, Coalesced Load, 뱅크 충돌 해결 등 GPU 하드웨어의 구조적 특성을 이해하는 것이 고성능 커널 작성에 중요함
- 점유율(Occupancy), 스레드 분기 최소화, 양자화(Quantization) 등 추가적인 고려사항이 실제 성능에 중요한 영향을 미침
GPU의 컴퓨트 및 메모리 계층 구조
- GPU는 일반적으로 메모리 대역폭보다 산술 연산 처리 속도가 훨씬 높음
- 예를 들어, NVIDIA A100은 약 19.5 TFLOPS(32비트 부동소수점) 성능을 내지만, 메모리 대역폭은 약 1.5TB/s 수준임
- 4바이트 데이터를 읽는 동안 수십 개의 계산을 처리할 수 있으므로, 데이터 이동이 성능의 병목임
- 글로벌 메모리(VRAM) 는 모든 데이터가 존재하는 느린 오프칩 메모리이며, Streaming Multiprocessor(SM) 는 컴퓨팅을 담당함
- 각 SM에는 고속 온칩 Shared Memory(SRAM) 가 있고, 여기서 프로그램이 직접 관리하는 캐시로 활용 가능함
- 스레드는 가장 작은 실행 단위이며, 각 스레드는 개별 레지스터 집합을 갖고 있음
- 32개의 스레드가 Warp를 이루며, Block은 동일 SM에서 실행될 스레드 그리드임
성능 구간: 메모리 바운드 vs 컴퓨트 바운드
- 커널의 성능은 메모리 바운드(데이터 이동 속도에 의해 제한) 또는 컴퓨트 바운드(SM 연산 능력에 의해 제한) 중 하나임
- 연산 집약도(AI) 는 Total FLOPs / Total Bytes Accessed로 정의되며, 이 값이 중요 지표가 됨
-
Roofline 모델: x축이 AI, y축이 FLOPS/s인 그래프에서 커널의 실현 성능을 나타냄
- AI가 낮아 메모리 바운드이면 대각선(메모리 대역폭 계단)
- AI가 높아 계산 바운드이면 수평선(최고 연산 성능 계단)
- A100의 Ridge Point는 19.5 TFLOPS / 1.5 TB/s ≈ 13 FLOPs/Byte임
- AI를 높이면 성능이 향상되며, 커널이 컴퓨트 바운드에 도달할 수 있음
연산 집약도 높이기 전략
- 간단한 모델: 스레드 1개가 C[i,j] 1개 계산 → AI = 0.25 (매우 낮음, 메모리 바운드)
- 스레드가 2x2 타일 계산 시에도 AI = 0.5 (여전히 낮음)
- AI를 높이기 위해서는 여러 스레드가 블록 단위로 Shared Memory에 대형 타일을 적재해 데이터 재사용을 극대화해야 함
- Block 내 스레드 협력을 통해 AI > 13로 증가시켜 컴퓨트 바운드에 진입 가능함
오버헤드 바운드 상태
- CPU(호스트)가 GPU에 작업을 할당하는 과정에서 오버헤드 발생 가능
- GPU 커널이 너무 작거나 많으면, GPU는 작업을 기다리며 대기하는 상황 발생
- 현대 프레임워크는 비동기 실행을 도입해, 커맨드 스트림을 미리 큐잉하여 오버헤드를 최소화함
성능 증진을 위한 두 가지 핵심 전략: Fusion과 Tiling
Operator Fusion (연산 합치기)
- 단순 연산(chain), 예: y = relu(x + 1)에서는 각 연산이 별도 커널로 동작하면 데이터가 글로벌 메모리를 왕복함
- Fusion은 여러 연산을 하나의 커널로 합쳐 중간 값을 글로벌 메모리에 저장하지 않고, 레지스터 내에서 연산 처리 후 최종 결과만 기록함
- 예시: Triton, torch.compile Inductor 등 JIT 컴파일러가 자동화 처리
Tiling (타일링)
- 행렬곱 등 복잡한 연산에서, 단일 스레드 모델로는 AI가 낮음
- 블록 단위로 타일을 나눈 후, 블록 내 모든 스레드가 협력해 데이터 타일을 Shared Memory에 적재, 대규모 데이터 재사용 구현
- 연산은 "Load(글로벌 -> Shared Memory) - Synchronize(동기화) - Compute(연산)" 3단계 패턴을 따름
Coalesced Load와 벡터화
- 글로벌 메모리에서 Shared Memory로 데이터를 옮길 땐, Coalesced Access(워프 32개 스레드가 연속된 128바이트 구간 접근)가 중요
- 벡터화(예: float4) 로 한 번에 여러 데이터 로드 시, 하드웨어 리소스 절약 및 메모리 대역폭 활용 극대화
- 데이터 정렬(alignment)이 필수이며, 행렬 내 바이트 수 K값이 4의 배수여야 효율적임
Shared Memory 뱅크와 뱅크 충돌
- Shared Memory는 32개의 독립된 뱅크로 구성되어, 워프 32개 스레드가 각기 다른 뱅크에 접근해야 충돌 없이 동작
- 행 단위 접근은 충돌 없음, 열 단위 접근은 충돌(같은 뱅크 접근) 발생
- B 타일은 "로드 및 전치" 전략으로 Shared Memory에 전치로 저장해, 연산 시 행 접근 중심으로 뱅크 충돌을 피함
고속 온칩 연산 패턴
기본 전략 1: 한 스레드가 한 output 계산
- BLOCK_DIM=32 제한 하에 AI 최대 8로, 컴퓨트 바운드 진입 불가
전략 2: 한 스레드가 여러 output 계산
- BLOCK_DIM=16, TILE_DIM=64로 설정 시, 한 스레드가 4x4 출력 계산 → AI=16
- AI>13이므로 A100 기준으로 compute-bound 성능 달성 가능
- Shared Memory에서 float4 등 벡터화 로드로 효율적 연산 가능
타일화의 실제 한계: 타일 양자화
- 행렬 크기가 타일 크기의 배수가 아니면, 경계 블록이 실제보다 큰 영역을 계산(불필요한 연산)하며 패딩 처리됨
- 경계의 스레드는 guard 조건으로 불필요한 메모리 접근은 막지만, 연산 루프는 동일하게 돌려 쓰레기 계산(예: C += A * 0) 발생
추가 성능 튜닝 요소
점유율(Occupancy)와 대기(Latency) 숨기기
- 워프가 메모리 읽기 등 장시간 대기 시, SM은 다른 워프로 즉각 전환해 유휴 시간을 줄임(대기 숨기기, latency hiding)
- 여러 Thread Block을 동시에 할당하면, 높은 점유율로 대기 시간 최소화
- Block이나 타일 크기가 너무 커지면 resident block 수가 줄고, 점유율 저하로 성능 저하 발생
스레드 분기 최소화
- 워프 내 if-else 조건 분기가 발생하면, 두 경로를 순차적으로 실행해 효과적인 성능 절반 수준으로 감소함
- min, max 등 branchless 코드로 분기 최소화 필요
양자화(Quantization)
- FP32 → FP16/BFP16 등 정밀도 축소 시, 메모리 이동량 및 처리 가능 데이터 수가 각각 2배로 증가함
- A100 기준 FP16 연산은 312 TFLOPS(상대적으로 FP32의 19.5 TFLOPS 대비 최대 16배 성능) 도달 가능
- 양자화로 Roofline상의 AI 오른쪽(메모리 효율)과 위쪽(최고 연산 성능) 동시 달성 가능
전체 요약
- GPU 성능의 본질적 한계는 메모리 대역폭과 온칩 연산 능력의 불균형에 기인함
- 성능 향상은 데이터 재사용 극대화(타일링) 및 중간 메모리 트래픽 최소화(Fusion) 로 달성
- 하드웨어 구조(워프, 뱅크, 코얼레스드 액세스, 동기화) 특성을 이해해야 고성능 커널 작성 및 최적화가 가능함
- 실전에서는 점유율, 분기 최소화, 양자화 등 추가적인 요소가 실질적 속도에 직접적인 영향 미침
- 고성능 GPU 연산 설계는 이론적 AI 향상, 하드웨어 특성 활용, 실제 데이터 배치 및 사이즈 대응 등 복합적인 고려가 필요함
Hacker News 의견
-
전체 프로그램 최적화가 컴파일러 레벨에서 얼마나 잘 되고 있는지 궁금증, 각각의 LLM 아키텍처를 하나씩 최적화하는 현재 방식이 뭔가 뒤처지는 느낌이라는 생각
-
같은 4070에서 llama.cpp와 vllm을 돌려서 더 많은 프롬프트를 배치로 처리하려고 시도 경험 공유, batch 8부터 llama.cpp는 심각하게 느려지고 GPU 사용률은 괜찮아 보여도 실제론 병목이 생긴 상황 설명, vllm은 훨씬 더 잘 처리함을 체감함
-
vllm이 paged kv 캐시와 GPU가 선호하는 fully coalesced 레이아웃을 사용해서 배치에 최적화된 성능 제공, 반면 llama.cpp는 단일 프롬프트에 좋은 flat 레이아웃이라서 batch 상황에서는 L2 메모리 접근 패턴이 깨져 속도 저하
-
llama.cpp에서 kv tensor를 [seq, head, dim]에서 [head, seq, dim] 형태로 interleave하면 vllm에서 fused attention kernel로 데이터 공급하는 방식을 따라가 2배 정도 연산 성능 바로 향상 경험 공유
-
병목의 원인은 GPU 자체가 아니라 shared memory 접근 방식과 global read를 어떻게 설계하느냐에 있음, vllm이 바로 그 점을 레이아웃 변경으로 공략
-
이런 병목 분석에 2일 넘게 걸렸고, GPU 활용 그래프만 봐선 알 수 없었으며 대부분 시행착오로 알게 된 점
-
이런 실험을 좀 더 수월하게, 핫 리로드 방식으로 반복할 수 있는 방법이 있는지 궁금증 제시
-
GPU가 병목이 아니라고 했지만, 실제로는 메모리 레이아웃의 비효율이 결국 GPU의 연산 효율을 낮추는 병목이었다는 지적
-
deepseek 직원이 어제 공개한 nano-vllm 프로젝트 언급, 1200줄밖에 안 되는데 vanilla vllm보다 더 빠른 성능 기록했다는 소식 공유 https://github.com/GeeeekExplorer/nano-vllm
-
llama.cpp에서 변경된 레이아웃을 pull request로 올렸는지 질문, 2배 향상은 모두에게 큰 이득이 될 수 있을 거라는 의견
-
ik_llama.cpp라는 프로젝트도 시도해보라고 추천 https://github.com/ikawrakow/ik_llama.cpp
-
-
좋은 정보가 담긴 아티클로, 이 내용이 NVIDIA가 GPU 아키텍처를 개발할 때 선택하는 요소들에 대한 이야기라는 의견 전달, 타사와의 차이점을 오해하지 말라고 강조
-
예를 들어 AMD Instinct MI300은 FP32에서 최대 160 TFLOPS와 6TB/s의 HBM3/3E 대역폭으로 ridge-point가 바뀌며 이는 A100의 13 FLOPs/byte의 두 배인 27 FLOPs/byte, 대용량 HBM메모리(128~256GB)는 tiling depth와 occupancy 간 현실적 트레이드 오프도 바꿔놓음, 단 이런 GPU는 비싸고, CUDA 미지원이라는 트레이드오프 존재
-
AMD가 컴퓨팅 소프트웨어에 더 신경 쓰기 전까지는 NVIDIA GPU만 존재감을 가질 수밖에 없다는 의견
-
-
스포일러로, 실제로 중요한 건 GPU 동작 원리 자체보다 머신러닝 계산에 어떻게 활용하는지라는 점 강조
- 사실상 내용은 CUDA의 일반적인 요약에 가깝고, relu 예제와 torch 언급 빼곤 머신러닝과 큰 상관 없는 구성이라는 지적
-
대비 색상 사용을 반드시 해야 한다는 의견, 가독성 강조
-
font-weight: 300 사용 경험 공유, 대다수 Mac 디자이너가 폰트 스무딩 옵션에 맞춰 개발해서 일반적으로는 "normal"로 보이게 설정하는데, Mac은 얇은 폰트도 반쯤 두껍게 보이게 처리, 그래서 디자이너들은 더 얇은 폰트로 "보통" 느낌을 내는 경향 언급, 관련 링크 공유 https://news.ycombinator.com/item?id=23553486
-
저자가 다크 모드로 편집하고 포맷하는 중일 수 있다는 추측, edge://flags/#enable-force-dark를 씌우면 링크가 잘 보인다는 점 언급
-
작성자가 링크와 코드 블록 내 주석은 읽는 데 특히 더 노력 필요했던 점 지적, 콘트라스트 증가 제안, 콘텐츠 품질 자체는 매우 훌륭했다는 평가
-
웹사이트가 텍스트에 알파 투명도를 써서 심각하게 콘트라스트를 떨어뜨리는 큰 실수라는 비판
-
-
이 글은 사실 Nvidia GPU의 기본적인 사실들에 좀 더 가까운 제목이 더 나을 것 같다는 제안, WARP 용어도 최신 Nvidia GPU의 특징이며, 2003년쯤의 Nvidia GPU는 비디오 게임 렌더링만을 위한 하드웨어라서, 오늘날의 범용 연산 GPU와는 완전히 다르다는 배경 설명, 결국 게시글 내용은 모든 GPU에 적용될 수 있는 일반적 설명은 아니라는 요약
-
정말 좋은 입문용 자료라서 감사하다는 의견, AI PC를 직접 조립할 때 GPU에 대해 며칠씩 조사했는데, 이 글이 반드시 알아야 할 핵심과 고부가가치 응용분야(생성형 AI)까지 잘 정리돼 있어 큰 도움됐다는 후일담, 특히 A100 GPU의 메모리 계층 구조 다이어그램이 매우 유용했다는 평가
-
ASCII 다이어그램 사용에 대한 의아함