TROISINH
BreakthroughsKV Cache & Inference

Prefix Caching — System prompt giống nhau? Tính 1 lần, dùng ngàn lần

Kỹ thuật Prefix Caching giúp giảm 90% chi phí inference bằng cách tái sử dụng KV cache cho phần prompt chung, biến GPU từ máy tính lại thành máy tra cứu.

Khi bạn triển khai hệ thống AI cho hàng nghìn người dùng — dù là chatbot RAG hay agentic workflow — 80-90% nội dung prompt lặp lại giống hệt nhau: system instructions, tool definitions, hay ngữ cảnh tài liệu. Nếu không có Prefix Caching, bạn đang trả tiền GPU để tính toán đi tính lại cùng một phép nhân ma trận hàng nghìn lần. Anthropic báo cáo giảm 90% chi phí và 85% độ trễ chỉ bằng cách đừng làm việc thừa.

Vấn đề

Trong inference LLM, giai đoạn prefill (xử lý prompt đầu vào) là kẻ giết chết Time-To-First-Token (TTFT). Với một agent thực thi multi-step reasoning, mỗi lượt gọi API đều mang theo cùng một khối system prompt 2K tokens và context RAG 8K tokens. Không có caching, GPU phải chạy forward pass qua 10K tokens đó cho mỗi request — dù nội dung chưa từng thay đổi.

Vấn đề trở nên nghiêm trọng với KV cache. Mỗi lần tính attention cho token thứ i, model cần Key (K) và Value (V) của tất cả token trước đó. Nếu bạn có 1000 request song song, mỗi request 10K tokens, bạn đang tính 10 triệu lần projection K/V cho cùng một đoạn text. Đây là sự lãng phí FLOPs khổng lồ và là lý do tại sao các hệ thống production trước đây phải "đốt" GPU để xử lý prompt dài, trong khi phần sinh token (decode) lại nhanh.

Ý tưởng cốt lõi

KV cache chính là "trạng thái tinh thần" của model sau khi đọc prompt. Khi hai người đọc cùng một chương sách, "hiểu biết" của họ về chương đó là giống hệt nhau. Tương tự, với transformer, nếu hai prompt chia sẻ prefix chung 1000 tokens đầu tiên, tensor K/V cho 1000 tokens đó là bit-for-bit identical — hoàn toàn giống nhau ở mức bit.

Tại sao? Vì causal masking. Token ở vị trí i chỉ có thể nhìn thấy các token từ 0 đến i. Nó không biết tương lai tồn tại. Do đó, K/V của token i chỉ phụ thuộc vào quá khứ, và nếu quá khứ giống nhau, K/V sinh ra cũng giống nhau. Đây là tính chất bất biến nhân quả (causal immutability).

Cơ chế thực hiện: Thay vì tính lại, ta lưu K/V của prefix vào bộ nhớ và "tái sử dụng" cho mọi request có chung prefix. Khi request mới đến, model bắt đầu tính từ token đầu tiên khác biệt, coi cached K/V như "lịch sử đã được xử lý".

Nhưng làm sao để chia sẻ bộ nhớ hiệu quả giữa nhiều request? Đây là lúc PagedAttention (vLLM) xuất hiện. Nó chia KV cache thành các block 16-token, quản lý qua "block table" — tương tự virtual memory trong hệ điều hành. 1000 request có thể trỏ đến cùng một vật lý block cho phần prefix chung, chỉ copy-on-write khi phần suffix khác nhau.

Cái bẫy byte-for-byte: Nhiều người tưởng "cùng nghĩa là cache hit". Sai. [{"a":1,"b":2}][{"b":2,"a":1}] khác nhau về token ID. Prefix Caching yêu cầu exact match từng byte — thứ tự key JSON, khoảng trắng, dấu xuống dòng đều phải giống hệt. Một ký tự khác là cache miss ngay lập tức.

That's it. Bạn không cần thay đổi kiến trúc model. Chỉ cần đừng tính lại những gì đã biết chắc.

Tại sao nó hoạt động

Toán học của Causal Additivity:

Trong attention mechanism, output tại vị trí i là tổng có trọng số của Value vectors:

\text{Attention}(Q_i, K_{\leq i}, V_{\leq i}) = \sum_{j=0}^{i} \text{softmax}\left(\frac{Q_i K_j^T}{\sqrt[d_k]}\right) V_j

Lưu ý rằng KjK_jVjV_j chỉ phụ thuộc vào tokens 0..j, không phụ thuộc vào tokens sau j. Do đó, với hai prompt chia sẻ prefix đến vị trí mm, ta có:

  • Kj(prompt1)=Kj(prompt2)K_j^{(prompt1)} = K_j^{(prompt2)}Vj(prompt1)=Vj(prompt2)V_j^{(prompt1)} = V_j^{(prompt2)} với mọi jmj \leq m

Cài đặt PagedAttention:

# Pseudocode minh họa block table
block_table = {
    "request_001": [block_0, block_1, block_5],  # block_0 shared
    "request_002": [block_0, block_1, block_9],  # share block_0,1 với request_001
}
# block_0 chứa KV của system prompt + tool definitions

Khi request mới đến, hệ thống hash prefix, tìm trong cache:

  • Cache hit: Load block IDs vào block table của request mới (zero-copy, chỉ copy pointer)
  • Cache miss: Chạy prefill thông thường, lưu K/V vào block mới

Chiến lược Eviction: GPU memory có hạn (H100 80GB chứa được khoảng 40K-50K tokens ở FP16). Khi đầy, LRU (Least Recently Used) hoặc LFU (Least Frequently Used) quyết định xóa block nào. Marconi (2024) đề xuất admission policies thông minh hơn cho hybrid LLMs.

Ý nghĩa thực tế

Hiệu năng thực tế:

  • Throughput: vLLM cộng đồng báo cáo 2-10× throughput improvement trên workload agent có prefix chung
  • Latency: DigitalOcean ghi nhận giảm 80% TTFT cho prompt dài; Anthropic giảm 85% độ trễ90% chi phí với prompt caching rõ ràng
  • Memory: Giảm áp lực bộ nhớ nhờ chia sẻ vật lý giữa request, cho phép batch size lớn hơn

So sánh với KV Cache cơ bản:

KV Cache thườngPrefix Caching
Phạm viNội bộ 1 requestChia sẻ cross-request
MemoryMỗi request cấp phát riêngCopy-on-write, shared blocks
TTFTTuyến tính với độ dài promptGần như instant nếu cache hit
Ràng buộcKhông cóByte-for-byte exact match

Giới hạn cần biết:

  • Chỉ prefill, không decode: Tăng tốc xử lý đầu vào, không giúp sinh token nhanh hơn
  • Cold start: Request đầu tiên vẫn phải trả giá đầy đủ; lợi ích tích lũy theo thời gian
  • Memory pressure: Prefix dài (4K+) × nhiều concurrent request nhanh chóng đầy H100
  • Exact match tyranny: Timestamp, JSON key order, hay một dấu cách thừa cũng phá vỡ cache

Đào sâu hơn

Paper gốc:

Cùng cụm (KV Cache & Inference):

Đọc tiếp:

  • Flash Attention — Tối ưu attention computation, bổ sung cho prefix caching ở layer thấp
  • EAGLE Speculative Decoding — Frontier method tăng tốc inference ở Level 2
  • GGUF & llama.cpp — Quantization giảm memory footprint cho KV cache, kết hợp với prefix caching trên consumer hardware

On this page