Study · GPU · Interactive
Where the bytes live,
and why decode waits for them. byte는 어디에 사는가,
그리고 decode는 왜 그 byte를 기다리는가.
A datacenter GPU is a small city built around one shortage: bytes per second. This is a guided tour of the NVIDIA A100-40GB — the floorplan, the memory ladder, the roofline, and a single LLM decode step traced byte by byte — ending at the number that rules inference: 14 GB ÷ 1.55 TB/s ≈ 9 ms per token. 데이터센터 GPU는 단 하나의 부족 자원, 초당 bytes를 중심으로 지어진 작은 도시다. NVIDIA A100-40GB를 따라가는 가이드 투어 — floorplan, memory 사다리, roofline, 그리고 byte 단위로 추적한 LLM decode 한 스텝 — 의 끝에는 추론을 지배하는 숫자 14 GB ÷ 1.55 TB/s ≈ 9 ms per token이 있다.
§1The itch: 99.5% of the machine goes missing문제의식: 기계의 99.5%가 사라진다
Rent the GPU this page is about — an NVIDIA A100 — and the datasheet promises 312 trillion floating-point operations per second. Now load a 7-billion-parameter chat model and generate text for a single user. Measured against that promise, the arithmetic units are doing ≈1.6 TFLOP/s — half a percent of what you paid for. At any given instant, 99.5% of the machine is doing nothing. 이 페이지의 주인공인 GPU — NVIDIA A100 — 를 빌리면 datasheet는 초당 312조 번의 부동소수점 연산을 약속한다. 이제 7B 파라미터 챗 모델을 올리고 사용자 한 명에게 텍스트를 생성해 보자. 그 약속에 견줘 실측하면 연산 유닛이 하는 일은 ≈1.6 TFLOP/s — 지불한 것의 0.5%다. 어느 순간을 잘라 봐도 기계의 99.5%는 놀고 있다.
Nothing in the obvious toolbox fixes this. A newer GPU with even more tensor cores makes single-user decode barely faster. The FLOPs were never the scarce resource — something else is, and until you can name that resource and put a number on it, every optimization aims at the wrong target. Naming it precisely is the goal here. 뻔한 도구상자 안에는 해결책이 없다. tensor core를 더 얹은 최신 GPU로 바꿔도 단일 사용자 decode는 거의 빨라지지 않는다. 애초에 부족한 자원이 FLOP이 아니었기 때문이다 — 다른 무언가가 부족하고, 그것의 이름과 숫자를 대기 전까지 모든 최적화는 엉뚱한 과녁을 겨눈다. 그 이름을 정확히 대는 것이 이 페이지의 목표다.
So which resource is actually scarce? 그렇다면 실제로 부족한 자원은 무엇인가?
§2Foundations: bytes, FLOPs, and one ratio기초: byte, FLOP, 그리고 비율 하나
A processor spends two currencies on every piece of work. The first is arithmetic: floating-point operations, FLOPs. The second is data movement: bytes carried from memory to the arithmetic units. They are priced separately — compute in FLOP/s, movement in bytes/s, which is called bandwidth. One further distinction matters. Latency is how long one byte takes to arrive, and it can be hidden by overlapping many requests in flight. Bandwidth is how many bytes arrive per second, and it cannot be hidden — if the computation needs the bytes, they must physically cross the wires. 프로세서는 모든 작업에 두 가지 통화를 지불한다. 첫째는 연산 — 부동소수점 연산, FLOP. 둘째는 데이터 이동 — memory에서 연산 유닛까지 실어 나르는 byte. 둘의 가격표는 따로 붙는다 — compute는 FLOP/s로, 이동은 bytes/s, 즉 bandwidth로. 구분 하나가 더 필요하다. Latency는 byte 하나가 도착하는 데 걸리는 시간이고, 요청 여러 개를 겹쳐 띄우면 숨길 수 있다. bandwidth는 초당 도착하는 byte의 양이고, 숨길 수 없다 — 연산에 그 byte가 필요하다면 물리적으로 전선을 건너와야 하기 때문이다.
Why not build memory that is both huge and fast? Physics forbids it. Storage next to the arithmetic (SRAM) is fast but burns die area per bit; dense storage (DRAM) must sit farther away, behind narrower connections. Every computer is therefore built as a hierarchy: a few fast kilobytes beside the math, gigabytes at a distance, each level trading capacity for speed. A datacenter GPU is the most extreme version of this bargain ever mass-produced. 크면서 동시에 빠른 memory를 만들면 되지 않을까? 물리학이 금지한다. 연산기 옆의 저장소(SRAM)는 빠르지만 bit당 die 면적을 태우고, 밀도가 높은 저장소(DRAM)는 더 좁은 연결 뒤, 더 먼 곳에 놓일 수밖에 없다. 그래서 모든 컴퓨터는 계층으로 지어진다: 연산기 옆의 빠른 몇 KB, 멀리 떨어진 몇 GB — 각 계층이 용량과 속도를 맞바꾼다. 데이터센터 GPU는 이 거래의 역사상 가장 극단적인 양산품이다.
One derived number decides which currency you run out of. Divide the machine's two peaks: 312 TFLOP/s ÷ 1.555 TB/s ≈ 200 FLOP per byte. That is the machine's balance point: to keep the arithmetic fully fed, a workload must perform about 200 FLOPs for every byte it pulls from main memory. A workload's own ratio — FLOPs performed per byte moved — is its arithmetic intensity (AI). Above ~200 the workload is compute-limited; below it, memory-limited, and the surplus FLOPs are free but useless. 어느 통화가 먼저 바닥나는지는 파생 숫자 하나가 결정한다. 기계의 두 peak을 나눠 보자: 312 TFLOP/s ÷ 1.555 TB/s ≈ 200 FLOP per byte. 이것이 기계의 균형점이다: 연산기를 계속 먹여 살리려면 main memory에서 가져오는 byte 하나당 약 200 FLOP을 수행해야 한다. 워크로드 자신의 비율 — 옮긴 byte당 수행한 FLOP — 이 arithmetic intensity(AI)다. ~200 위면 compute-limited, 아래면 memory-limited이고, 남는 FLOP은 공짜지만 쓸모가 없다.
Now the workload. LLM decode produces one token at a time, and each token multiplies a single activation vector against every weight matrix in the model — a matrix–vector product. Each 2-byte FP16 weight is read once and used for exactly 2 FLOPs (one multiply, one add). That is an arithmetic intensity of about 1 — one, against a machine that wants 200. Hold that pair of numbers; the rest of the page is watching it play out in silicon. 이제 워크로드 차례다. LLM decode는 한 번에 token 하나를 만들고, token 하나는 activation 벡터 하나를 모델의 모든 weight 행렬과 곱한다 — matrix–vector 곱이다. 2-byte FP16 weight 하나는 한 번 읽혀 정확히 2 FLOP(곱하기 하나, 더하기 하나)에 쓰인다. arithmetic intensity로는 약 1 — 200을 원하는 기계에 1을 들고 도착하는 셈이다. 이 숫자 쌍을 기억해 두자; 페이지의 나머지는 이것이 실리콘 위에서 펼쳐지는 과정이다.
What does this shortage look like on the actual silicon? 이 부족은 실제 실리콘 위에서는 어떤 모습인가?
§3The die, mapped: compute is an islandDie 지도: compute는 섬이다
We now have the vocabulary — bytes, bandwidth, a balance point of ≈200 — but those are abstractions. The missing 99.5% from §1 has a physical address. Here is the A100's floorplan; the geography alone tells most of the story. 이제 어휘는 갖췄다 — byte, bandwidth, ≈200이라는 균형점 — 하지만 아직은 추상이다. §1에서 사라진 99.5%에는 물리적 주소가 있다. A100의 floorplan을 보자; 지리만으로도 이야기의 대부분이 설명된다.
The floorplan: compute is an island, memory is the ocean around it Floorplan: compute는 섬이고, memory는 그 주위의 바다다
Simplified A100 die. Hover any region for its numbers; click an SM to zoom into its internals on the right. 단순화한 A100 die. 영역에 hover하면 해당 숫자가 나오고, SM을 클릭하면 오른쪽에서 그 내부를 확대해 볼 수 있다.
826 mm² of silicon, one bottleneck
108 streaming multiprocessors (SMs) share a 40 MB L2 cache and five active HBM2e stacks delivering 1.555 TB/s. Every weight an LLM touches must cross from the flanking HBM into this island — hover a region to see its numbers.
Per SM: 4 processing blocks, each with a warp scheduler, a 64 KB register-file partition (256 KB total) and one tensor core (4 per SM, 432 on the die). All four share 192 KB of combined L1/shared storage — up to 164 KB usable as software-managed shared memory. SM당 processing block 4개 — 각각 warp scheduler 1개, 64 KB register-file 파티션(총 256 KB), tensor core 1개(SM당 4개, die 전체 432개)를 가진다. 네 블록이 L1/shared 통합 192 KB를 공유하며, 최대 164 KB를 소프트웨어가 관리하는 shared memory로 쓸 수 있다.
The A100 die packs 108 SMs, each with 4 third-generation tensor cores, a 256 KB register file, and 192 KB of combined L1/shared storage (up to 164 KB configurable as shared memory). All of them talk to a 40 MB L2 split into two partitions, and the L2 talks to five active HBM2e stacks sitting physically beside the die on a silicon interposer. The geography is the point: FLOPs are manufactured in the middle, but every byte of an LLM's weights lives off-die and must be imported at 1.555 TB/s — the width of that bridge, not the size of the island, decides how fast you decode. A100 die에는 108개의 SM이 들어 있고, 각 SM은 3세대 tensor core 4개, 256 KB register file, L1/shared 통합 192 KB(최대 164 KB를 shared memory로 설정 가능)를 가진다. 이들 모두는 두 파티션으로 나뉜 40 MB L2와 통신하고, L2는 실리콘 interposer 위 die 바로 옆에 물리적으로 놓인 5개의 활성 HBM2e 스택과 통신한다. 핵심은 이 지리다: FLOP은 가운데 섬에서 만들어지지만, LLM weight의 모든 byte는 die 밖에 살며 1.555 TB/s로 수입되어야 한다 — decode 속도를 정하는 것은 섬의 크기가 아니라 그 다리의 폭이다.
The die is not empty of memory — registers, shared memory, L2. Why can't the model simply live on-die? die 안에도 memory는 있다 — register, shared memory, L2. 그런데 왜 모델이 그냥 on-die에 살 수 없을까?
§4The memory ladder: capacity for speed, ×1000 at a timeMemory 사다리: 용량과 속도를 1000배 단위로 맞바꾸다
The floorplan showed several pools of on-die storage. Whether they can rescue decode comes down to two numbers per pool: how big, and how fast. Put the four levels side by side — on a log scale — and a brutal trade appears. floorplan에는 on-die 저장소 풀이 여럿 보였다. 이들이 decode를 구할 수 있는지는 풀마다 숫자 두 개 — 얼마나 크고, 얼마나 빠른가 — 로 결정된다. 네 계층을 log scale로 나란히 놓으면 잔혹한 거래 조건이 드러난다.
The ladder: every step up costs ~1000× capacity, buys ~10× speed 사다리: 한 칸 오를 때마다 용량 ~1000×를 내주고 속도 ~10×를 산다
Log-scale bars — equal visual steps are multiplicative. Hover a level for what actually fits there during LLM inference. Log-scale 막대 — 시각적으로 같은 간격이 곱셈 배수다. 각 계층에 hover하면 LLM 추론 중 실제로 거기 들어가는 것이 보인다.
Each level trades roughly three orders of magnitude of capacity for one order of magnitude of bandwidth and latency. The numbers worth memorizing: shared memory aggregates to ≈19.5 TB/s (about 12× HBM), and the register file, at ≈27 MB die-wide, is a faster and nearly as large pool as L2 — which is why high-performance GEMM kernels are really register-file choreography. The ladder also explains why caching weights is hopeless: the largest on-die pool (L2, 40 MB) barely holds one 4096×4096 FP16 matrix (33.5 MB) out of a 13.5 GB model. 각 계층은 대략 용량 세 자릿수를 bandwidth·latency 한 자릿수와 맞바꾼다. 외울 가치가 있는 숫자들: shared memory는 합산 ≈19.5 TB/s(HBM의 약 12×)이고, register file은 die 전체 ≈27 MB로 L2에 버금가는 크기이면서 더 빠른 풀이다 — 고성능 GEMM kernel이 사실상 register-file 안무인 이유다. 이 사다리는 weight caching이 왜 가망 없는지도 설명한다: 가장 큰 on-die 풀(L2, 40 MB)이 13.5 GB 모델 중 4096×4096 FP16 행렬 하나(33.5 MB)를 간신히 담는다.
If the bytes must stream no matter what, can we predict a workload's speed from its FLOPs and bytes alone? 어차피 byte가 스트리밍되어야 한다면, 워크로드의 속도를 FLOP과 byte만으로 예측할 수 있을까?
§5The roofline: the whole argument in one pictureRoofline: 지금까지의 논증을 그림 한 장으로
Fair warning: this is the hardest section of the page — take it slowly. The roofline model compresses everything so far into a single picture: one axis of arithmetic intensity, two ceilings, and every workload's speed limit readable at a glance. It is the one diagram worth internalizing before doing any performance work on GPUs. 미리 말해 두면, 여기가 이 페이지에서 가장 어려운 구간이다 — 천천히 가자. roofline 모델은 지금까지의 모든 것을 그림 한 장으로 압축한다: arithmetic intensity라는 축 하나, 천장 두 개, 그리고 한눈에 읽히는 모든 워크로드의 속도 한계. GPU 성능 작업을 하기 전에 체화할 가치가 있는 단 하나의 다이어그램이다.
The roofline: two ceilings, and decode lives under the slanted one Roofline: 천장은 두 개, decode는 기울어진 쪽 아래에 산다
Achievable FLOP/s = min(peak compute, bandwidth × arithmetic intensity). Log–log axes. Drag the batch slider and switch weight precision to move the ● marker. 달성 가능 FLOP/s = min(peak compute, bandwidth × arithmetic intensity). Log–log 축. batch 슬라이더를 움직이고 weight 정밀도를 바꾸면 ● 마커가 이동한다.
14 GB weights ÷ 1.555 TB/s ≈ 9.0 ms/token → ~111 tok/s At B=1 every generated token must read all weights once. Quantize FP16→INT4 and the same division reads 3.5 GB ÷ 1.555 TB/s ≈ 2.3 ms → ~440 tok/s: compression is a bandwidth multiplier, not a compute trick. B=1에서는 생성되는 token마다 모든 weight를 한 번씩 읽어야 한다. FP16→INT4로 quantize하면 같은 나눗셈이 3.5 GB ÷ 1.555 TB/s ≈ 2.3 ms → ~440 tok/s가 된다: 압축은 compute 트릭이 아니라 bandwidth 곱셈기다.
Arithmetic intensity (AI) is FLOPs performed per byte moved from HBM. The machine's balance point is 312 TFLOP/s ÷ 1.555 TB/s ≈ 200 FLOP/byte. Prefill GEMMs (thousands of tokens sharing each weight read) sit far right, compute-bound. Single-batch decode is a GEMV: 2 FLOPs per 2-byte weight, AI ≈ 1, achieving ≈1.6 TFLOP/s — half a percent of peak, and no kernel cleverness can fix it because the bytes are mandatory. Batching amortizes weight reads (AI ≈ B), which is why serving systems fight for large batches; attention over a long KV cache, however, stays at AI ≈ 1 even when batched, because each sequence reads its own KV — this is exactly the seam that KV-cache compression and quantization attack. Arithmetic intensity(AI)는 HBM에서 옮긴 byte당 수행한 FLOP이다. 이 기계의 균형점은 312 TFLOP/s ÷ 1.555 TB/s ≈ 200 FLOP/byte. Prefill GEMM(수천 개의 token이 weight 읽기를 공유)은 오른쪽 끝 compute-bound에 앉는다. Batch 1 decode는 GEMV다: 2-byte weight당 2 FLOP, AI ≈ 1, 달성 성능 ≈1.6 TFLOP/s — peak의 0.5%이고, byte 이동이 필수이므로 어떤 kernel 묘수로도 고칠 수 없다. Batching은 weight 읽기를 분할상환하므로(AI ≈ B) serving 시스템이 큰 batch를 위해 싸우는 것이고, 반면 긴 KV cache에 대한 attention은 batch를 해도 AI ≈ 1에 머문다 — 각 sequence가 자기 KV를 읽기 때문이다. 바로 이 지점이 KV-cache 압축과 quantization이 공략하는 틈이다.
Ceilings and slopes are still abstract. What do the mandatory bytes look like, one matrix at a time, while a single token is made? 천장과 기울기는 여전히 추상적이다. token 하나가 만들어지는 동안, 그 필수 byte들은 행렬 단위로 어떤 모습일까?
§6One decode step, traced byte by byteDecode 한 스텝을 byte 단위로 추적하다
The roofline becomes concrete the moment you watch one token being manufactured. Below is the full byte-ledger of a single decode step for a 7B-class model — every matrix that must cross the bridge, in order, with a running meter of bytes and FLOPs. token 하나가 제조되는 과정을 지켜보는 순간 roofline은 구체가 된다. 아래는 7B급 모델의 decode 한 스텝에 대한 완전한 byte 장부다 — 다리를 건너야 하는 모든 행렬을 순서대로, byte와 FLOP 계기판과 함께.
One decode step, traced: 15.4 GB in, one token out Decode 한 스텝 추적: 15.4 GB가 들어가고 token 하나가 나온다
Llama-7B-class model (d=4096, 32 layers, FFN 11008, vocab 32k), FP16, 4096 tokens of context, batch 1. Step through what actually moves. Llama-7B급 모델 (d=4096, 32 layers, FFN 11008, vocab 32k), FP16, context 4096 tokens, batch 1. 실제로 무엇이 움직이는지 단계별로 따라가 보자.
For a 7B-class FP16 model with 4k context at batch 1, one token costs ≈13.5 GB of weights + ≈2.15 GB of KV cache + a 262 MB LM head read — ≈15.4 GB of traffic for ≈15 GFLOP of work. Divide: 15.4 GB ÷ 1.555 TB/s ≈ 9.9 ms, an upper bound near ~100 tok/s, while the same FLOPs would take the tensor cores ~0.05 ms. Everything in efficient inference follows from this division — quantize weights and KV (shrink the numerator), batch requests (amortize it), or restructure attention so fewer bytes are mandatory. My research lives in that numerator. 4k context, batch 1의 7B급 FP16 모델에서 token 하나의 비용은 weight ≈13.5 GB + KV cache ≈2.15 GB + LM head 읽기 262 MB — ≈15 GFLOP의 연산을 위한 ≈15.4 GB의 트래픽이다. 나눠 보면: 15.4 GB ÷ 1.555 TB/s ≈ 9.9 ms, 약 ~100 tok/s 근처의 상한이고, 같은 FLOP은 tensor core로 ~0.05 ms면 끝난다. 효율적 추론의 모든 것이 이 나눗셈에서 나온다 — weight와 KV를 quantize하고(분자 줄이기), 요청을 batching하고 (분자 분할상환), 필수 byte 자체가 줄도록 attention을 재구성하는 것. 내 연구는 그 분자 안에 산다.
Fine print on the numbers숫자에 대한 작은 글씨
Peak specs (108 SMs, 40 GB @ 1.555 TB/s, 40 MB L2, 312 TFLOP/s dense BF16/FP16 tensor core, 192 KB L1+shared per SM with ≤164 KB shared) are from NVIDIA's A100 white paper for the SXM 40 GB part. On-die bandwidth and latency figures (L2 and shared-memory throughput, ~20/200/450 ns tiers) are microbenchmark-derived ballparks that vary with access pattern and clocks — treat them as order-of-magnitude anchors, not datasheet promises. Tokens/s figures are bandwidth ceilings; real kernels land 70–90% of the way there. Peak 스펙(108 SMs, 40 GB @ 1.555 TB/s, 40 MB L2, dense BF16/FP16 tensor core 312 TFLOP/s, SM당 L1+shared 192 KB 중 ≤164 KB shared)은 SXM 40 GB 부품에 대한 NVIDIA A100 white paper 기준이다. On-die bandwidth·latency 수치(L2와 shared-memory throughput, ~20/200/450 ns 단계)는 microbenchmark에서 나온 어림값으로 접근 패턴과 클럭에 따라 달라진다 — datasheet의 약속이 아니라 자릿수 앵커로 취급하라. tok/s 수치는 bandwidth 상한이며, 실제 kernel은 그 70–90% 지점에 도달한다.
If the bytes set the floor, the only way forward is to change the bytes. Where is the field pushing? byte가 하한을 정한다면, 나아갈 길은 그 byte 자체를 바꾸는 것뿐이다. 지금 이 분야는 어디를 밀고 있을까?
§7Where this goes: attacking the division앞으로의 방향: 나눗셈을 공격하다
Every live direction in efficient inference is an attack on the division 15.4 GB ÷ 1.555 TB/s. Route one shrinks the numerator: quantize the weights and the KV cache — see the reviews of KIVI, KVQuant and TurboQuant — or decide some KV bytes were never needed at all (H2O, DuoAttention). Route two changes which bytes are mandatory, by redesigning the architecture around a smaller or constant-size state (Gated DeltaNet, MiniMax-01, MobileLLM). Route three changes the decode pattern itself so each weight-read produces many tokens — diffusion language models (LLaDA, Fast-dLLM). 효율적 추론의 살아 있는 방향은 전부 15.4 GB ÷ 1.555 TB/s라는 나눗셈에 대한 공격이다. 첫째 길은 분자를 줄인다: weight와 KV cache의 quantization — KIVI, KVQuant, TurboQuant 리뷰 참고 — 또는 애초에 필요 없던 KV byte를 골라내는 것(H2O, DuoAttention). 둘째 길은 필수 byte의 목록 자체를 바꾼다 — 더 작거나 고정 크기인 state를 중심으로 아키텍처를 재설계하는 것 (Gated DeltaNet, MiniMax-01, MobileLLM). 셋째 길은 decode 패턴 자체를 바꿔 weight 한 번 읽기로 여러 token을 만들어 낸다 — diffusion language model이다 (LLaDA, Fast-dLLM).
None of it is settled. Long-context attention still reads its own KV at AI ≈ 1 even at large batch, and the hardware keeps making the problem sharper: each GPU generation grows FLOPs faster than bandwidth, so the ridge point keeps moving right. The division at the top of this page is not a snapshot of 2020 silicon — it is the trendline the whole field is racing. 어느 것도 끝난 이야기가 아니다. 긴 context의 attention은 batch를 키워도 여전히 AI ≈ 1로 자기 KV를 읽고, 하드웨어는 문제를 계속 더 날카롭게 만든다: GPU 세대마다 FLOP이 bandwidth보다 빨리 자라 ridge point가 오른쪽으로 밀린다. 이 페이지 맨 위의 나눗셈은 2020년 실리콘의 스냅샷이 아니라, 이 분야 전체가 경주하고 있는 추세선이다.
Going deeper
Pull the thread실을 더 당겨보기
NVIDIA A100 white paper
The datasheet behind every number in the die map — SM diagram, memory subsystem, tensor-core formats.Die map의 모든 숫자 뒤에 있는 datasheet — SM 다이어그램, memory 서브시스템, tensor-core 포맷.
The modelRoofline (Williams et al., 2009)
The original insightful-performance-model paper — two ceilings, one x-axis, endless mileage.통찰형 성능 모델의 원조 논문 — 천장 두 개, x축 하나, 끝없는 활용도.
Back of envelopeTransformer inference arithmetic
kipply's walkthrough of the same bytes-per-token accounting done here, extended to multi-GPU serving.여기서 한 것과 같은 token당 byte 회계를 multi-GPU serving까지 확장한 kipply의 워크스루.
Hierarchy in actionFlashAttention
What "IO-aware" means in practice: restructure attention around SMEM tiles so the mandatory HBM bytes shrink."IO-aware"가 실전에서 뜻하는 것: SMEM tile 중심으로 attention을 재구성해 필수 HBM byte를 줄인다.
StudyBack to the bench
More interactive tear-downs — model anatomy, quantization pipelines, and the rest of the GPU track.더 많은 인터랙티브 분해 노트 — 모델 해부, quantization 파이프라인, GPU 트랙의 나머지.
ResearchWhere this bites
KV-cache compression and diffusion-LLM acceleration — my research is an attack on the numerator of that division.KV-cache 압축과 diffusion-LLM 가속 — 내 연구는 그 나눗셈의 분자에 대한 공격이다.