Lensa ML

KV Cache

Langkah 1 dari 6: Satu Token demi Satu

Setiap query baru membaca semua key sebelumnya

Hanya prompt: query dari token terakhir membaca semua keytoken berikutnya → "Paris"ThecapitalofFranceispromptquery
Panjang urutan
5
Key yang dibaca query ini
5
Token berikutnya
"Paris"
Skor q·k sejauh ini
5

Prompt ini berisi 5 token. Query dari "is" membaca semua 5 key dan value, lalu memprediksi "Paris".

Tekan Putar, atau Langkah untuk maju satu token demi satu, lalu perhatikan busur dari token terbaru. Setiap busur adalah satu skor query-key, q · k / √d, dengan d = 128 per head. Causal mask-lah yang membuat caching aman: menambahkan token tidak pernah mengubah key dan value posisi sebelumnya, karena posisi itu tidak bisa melihat token baru. Pada bidirectional encoder seperti BERT, menambahkan satu kata mengubah semua representasi sebelumnya, jadi tidak ada yang bisa dipakai ulang. Lanjutan setelah prompt ini ilustratif, bukan output model sungguhan.

Langkah 2 dari 6: Kerja yang Terbuang

Menghitung ulang prefix di setiap langkah

Token dihasilkan16
Panjang prompt P5
Vector K/V dihitung per langkah (satu baris per langkah, per layer)Tanpa cacheposisi → (total 20)Dengan KV cacheposisi → (total 20)dihitung, barudihitung ulangdibaca dari cache
K/V dihitung, tanpa cache
200
K/V dihitung, dengan cache
20
Rasio
10.0×

Pada 16 langkah, pemborosannya tumbuh mengikuti kuadrat panjangnya: 200 pasangan berbanding 20, 10,0 kali lipat kerja, di setiap layer dari 32 layer model.

Geser Token dihasilkan dari 1 sampai 64 dan Panjang prompt P dari 1 sampai 16. Tanpa cache, langkah s menghitung ulang key dan value untuk semua P + s − 1 posisi, jadi T langkah membutuhkan T · P + T(T − 1)/2 proyeksi per layer. Dengan cache, biayanya P + T − 1. Pada P = 5 dan T = 64, angkanya 2.336 berbanding 68, sekitar 34 kali lipat. Attention sendiri tetap membaca setiap key di cache, jadi cache menghilangkan proyeksi yang dihitung ulang, bukan biaya attention yang terus bertambah.

Langkah 3 dari 6: Menyimpan Key dan Value di Cache

Tambahkan satu k dan v, pakai ulang sisanya

Prefill: tulis 5 token prompt ke cachetokenK cacheV cachesoftmax(q·k), q dari "is"The7%capital23%of33%France5%is31%Nilai vector dan skor attention bersifat ilustratif
Panjang cache
5
K/V baru dihitung
5
Dot product q·k
5
Cache, urutan ini
2.5 MiB

Prefill menghitung k dan v untuk semua 5 token prompt dalam satu pass dan menyimpannya. Query dari token terakhir pada prompt lalu memberi skor ke semua 5 key di cache.

Tekan Putar atau Langkah dan lihat satu baris k dan satu baris v masuk ke cache di setiap langkah. Dalam praktik, cache berupa sepasang tensor per layer berbentuk [batch, kv_heads, tokens, head_dim], dan setiap langkah decode menulis satu irisan baru di sepanjang sumbu token. Hanya token terbaru yang melewati proyeksi Q, K, dan V serta feed-forward layer. Informasi posisi diterapkan sebelum disimpan: dengan rotary embedding (RoPE), setiap key dirotasi sesuai posisinya satu kali lalu disimpan dalam bentuk yang sudah dirotasi.

Langkah 4 dari 6: Prefill vs Decode

Compute-bound versus memory-bound

Panjang prompt512
Token output256
TFLOP/s yang bisa dicapai (GPU ilustratif: 300 TFLOP/s, 2 TB/s)0.11101001k10k110100FLOP per byte yang dibaca dari memori (skala log)memory-boundcompute-bounddecodeprefill
Token di prefill
512
Langkah decode
255
Dibaca per langkah decode
13.9 GB
FLOP/byte prefill
502

Prefill mengolah 512 token dalam satu pass dengan sekitar 502 FLOP per byte (compute-bound, kira-kira 23 ms di GPU ini). Masing-masing dari 255 langkah decode membaca hingga 13,9 GB untuk satu token, sekitar 6,9 ms per langkah (waktu ilustratif).

Geser Panjang prompt dan Token output, lalu bandingkan kedua titik pada roofline. Prefill mengalikan setiap matriks weight dengan semua P token prompt sekaligus, jadi setiap byte weight yang dibaca mengerjakan sekitar P operasi floating-point. Decode memakai ulang setiap byte hanya untuk satu token, sekitar 1 FLOP per byte, jauh di bawah ridge sekitar 150 pada GPU ilustratif ini. Itu sebabnya waktu per token output mengikuti bandwidth memori, dan batching banyak pengguna menaikkan throughput: satu kali baca weight melayani semua urutan dalam batch.

Langkah 5 dari 6: Tagihan Memori

Ukuran cache tumbuh mengikuti context dan batch

Panjang context (token)4.096
Batch size (urutan)1
2 × 32 layer × 32 head × 128 × 2 B = 512 KiB per token× 4.096 token × 1 urutan = 2.1 GBGPU 24 GBGPU 80 GBweight 13.5 GBKV cache 2.1 GBfp16, model chat 7B; aktivasi dan runtime overhead tidak dihitung
KV cache
2.1 GB
Weight
13.5 GB
Total
15.6 GB
Muat di
GPU 24 GB

Cache berukuran 2,1 GB, lebih kecil dari weight yang 13,5 GB, dan totalnya muat di GPU 24 GB.

Geser Panjang context dari 512 sampai 32.768 dan Batch size dari 1 sampai 64. Cache per token adalah 2 × layers × kv_heads × head_dim × bytes: 2 × 32 × 32 × 128 × 2 = 524.288 byte untuk model 7B, atau 0,5 MiB. Karena itu, urutan berisi 4.096 token butuh 2 GiB (2,1 GB). Weight dibayar sekali, sedangkan cache dibayar per urutan, jadi batch size yang bisa dijalankan server kira-kira sama dengan memori bebas dibagi cache untuk satu urutan. Aktivasi dan runtime buffer belum termasuk.

Langkah 6 dari 6: Mengecilkan Cache

Lebih sedikit head, bit, dan token

Key/value head
Presisi cache
Sliding window
32 query head32 key/value head (MHA), masing-masing 1 query headMHA, fp16, context penuh: 68.7 GBPengaturan ini: 68.7 GB512 KiB/token × 16.384 token × 8 urutan (context 16.384 token)
Per token
512 KiB
Token di cache
16.384
Ukuran cache
68.7 GB
Lebih kecil
1×

Multi-head attention penuh pada fp16 menyimpan 68,7 GB untuk 8 urutan berisi 16.384 token. Pilih lebih sedikit key/value head, int8, atau window untuk mengecilkannya. PagedAttention (vLLM) mengatasi pemborosan yang lain: cache disimpan dalam blok kecil yang dialokasikan sesuai kebutuhan, jadi tidak ada memori yang dicadangkan untuk token yang tidak pernah dicapai suatu urutan.

Ganti Key/value head, Presisi cache, dan Sliding window, lalu perhatikan ukuran cache. Multi-query attention (Shazeer, 2019) memakai satu key/value head bersama untuk semua query head. Grouped-query attention (Ainslie et al., 2023) memakai beberapa kelompok. Llama 2 70B memakai 8 KV head, sedangkan model 7B tetap memakai multi-head attention penuh. Keduanya adalah pilihan arsitektur yang dibuat sebelum atau selama training, begitu juga sliding window, yang dipakai Mistral 7B dengan 4.096 token. Cache int8 atau berpresisi lebih rendah bisa diterapkan saat serving, biasanya dengan sedikit penurunan akurasi.

Apa itu KV cache dalam inferensi LLM?

KV cache adalah memori tempat model bahasa decoder-only menyimpan vector key dan value dari setiap token yang sudah diproses, sehingga token baru hanya perlu menghitung key dan value-nya sendiri. Saat menghasilkan teks, model mengeluarkan satu token per forward pass, dan query setiap token baru harus membaca semua token sebelumnya. Tanpa cache, setiap pass akan menghitung ulang key dan value untuk seluruh prefix.

Semua server LLM produksi memakai KV cache, mulai dari Hugging Face Transformers hingga vLLM, TensorRT-LLM, dan llama.cpp. Cache mengubah proses menghasilkan teks dari komputasi ulang yang kuadratik menjadi satu pass pendek per token, tetapi memakan memori yang tumbuh mengikuti panjang context dan batch size. Memori itu, yang sering lebih besar dari weight model, membatasi jumlah pengguna yang bisa dilayani satu GPU sekaligus dan panjang context yang bisa ditampungnya. Karena itulah ada grouped-query attention, cache quantization, dan paged memory.

Ide utama

  • KV cache menyimpan key dan value setiap token sehingga keduanya dihitung sekali, bukan di setiap langkah.
  • Causal mask menjamin key dan value di cache tidak pernah berubah saat token baru ditambahkan.
  • Prefill mengolah prompt secara paralel dan bersifat compute-bound, sedangkan decode mengeluarkan satu token per langkah dan bersifat memory-bound.
  • Ukuran cache sama dengan 2 × layers × kv_heads × head_dim × bytes × tokens × batch.
  • Untuk model 7B pada fp16, cache memakan 0,5 MiB per token, jadi context panjang dan batch besar bisa melampaui weight.
  • Grouped-query attention, multi-query attention, cache quantization, dan sliding window mengecilkan cache, dan paged allocation mencegah memori terbuang.

Pertanyaan umum

Bagaimana cara menghitung ukuran KV cache?

Kalikan 2 (satu key dan satu value) × layer × KV head × dimensi head × byte per nilai untuk mendapatkan cache per token, lalu kalikan dengan panjang context dan batch size. Llama 2 7B punya 32 layer, 32 KV head, dan dimensi head 128, jadi pada fp16 butuh 2 × 32 × 32 × 128 × 2 = 524.288 byte, atau 0,5 MiB, per token: 2 GiB untuk satu urutan 4.096 token dan 16 GiB untuk batch berisi 8. Llama 3 8B memakai grouped-query attention dengan 8 KV head, jadi rumus yang sama menghasilkan 128 KiB per token, seperempatnya. Baca jumlah layer, head, dan dimensi dari config.json model (num_hidden_layers, num_key_value_heads, dan hidden_size dibagi num_attention_heads).

Apakah KV cache mengubah output model?

Tidak. Dengan aritmetika eksak, komputasi dengan dan tanpa cache menghasilkan logit yang sama, karena cache hanya menyimpan nilai yang kalau tidak disimpan akan dihitung ulang. Perbedaan kecil bisa muncul dari urutan floating-point pada kernel yang berbeda. Kuantisasi cache ke int8 atau lebih rendah adalah pendekatan dan bisa sedikit menggeser output.

Apa itu prompt caching?

Prompt caching, disebut juga prefix caching, menyimpan KV cache dari prefix yang sama, misalnya system prompt panjang atau dokumen, lalu memakainya ulang di banyak request. Request baru yang diawali token yang sama melewati prefill untuk token itu. Karena itulah penyedia API sering menagih token input yang sudah di-cache dengan harga lebih rendah.

Kenapa token pertama lebih lambat daripada sisanya?

Token pertama menunggu prefill, yang menjalankan seluruh prompt lewat model sebelum output apa pun muncul. Jeda ini disebut time to first token, dan makin panjang prompt, makin lama jedanya. Token berikutnya masing-masing butuh satu langkah decode, jadi kecepatannya terutama bergantung pada bandwidth memori dan batch size.

Apa itu PagedAttention?

PagedAttention, diperkenalkan bersama sistem serving vLLM oleh Kwon et al. (2023), menyimpan KV cache dalam blok berukuran tetap, bukan satu buffer contiguous per urutan. Blok dialokasikan saat urutan bertambah panjang dan bisa dipakai bersama oleh urutan yang punya prefix yang sama. Cara ini menghilangkan sebagian besar memori yang terbuang karena mencadangkan ruang untuk panjang maksimum, sehingga server bisa memasukkan lebih banyak request ke dalam batch.

Penjelasan terkait

Referensi

  1. Pope et al. (2022), "Efficiently Scaling Transformer Inference"
  2. Shazeer (2019), "Fast Transformer Decoding: One Write-Head is All You Need"
  3. Ainslie et al. (2023), "GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints"
  4. Kwon et al. (2023), "Efficient Memory Management for Large Language Model Serving with PagedAttention"

Terakhir diperbarui