การเพิ่มประสิทธิภาพ KV Cache: ประสิทธิภาพหน่วยความจำสำหรับ LLM ในระดับ Production
อัปเดตวันที่ 11 ธันวาคม 2025
อัปเดตธันวาคม 2025: การ inference แบบดั้งเดิมสูญเสียหน่วยความจำ KV cache 60-80% จากการกระจัดกระจาย PagedAttention ของ vLLM ลดการสูญเสียลงเหลือต่ำกว่า 4% ทำให้ throughput เพิ่มขึ้น 2-4 เท่า โมเดล 70B ที่มี context 8K ต้องการ cache ~20GB ต่อคำขอ และ ~640GB สำหรับ batch ขนาด 32 ตอนนี้ KV cache มักใช้หน่วยความจำมากกว่า model weights เสียอีก เทคนิคการเพิ่มประสิทธิภาพช่วยให้สามารถใช้ context ที่ยาวขึ้นและ batch ที่ใหญ่ขึ้นบนฮาร์ดแวร์ที่มีอยู่
ระบบ inference ของ LLM สูญเสียหน่วยความจำ KV cache ที่จัดสรรไว้ 60-80% ผ่านการกระจัดกระจายและการจัดสรรเกิน¹ การสูญเสียนั้นแปลงโดยตรงเป็น throughput ที่ลดลง ต้นทุนที่สูงขึ้น และข้อจำกัดเทียมของความยาว context PagedAttention ที่นำเสนอโดย vLLM ลดการสูญเสีย KV cache ลงเหลือต่ำกว่า 4% ทำให้ throughput เพิ่มขึ้น 2-4 เท่า ซึ่งเปลี่ยนแปลงเศรษฐศาสตร์ของ production inference² การทำความเข้าใจเทคนิคการเพิ่มประสิทธิภาพ KV cache ช่วยให้องค์กรสามารถใช้ประโยชน์จาก GPU ได้สูงสุดและให้บริการผู้ใช้ได้มากขึ้นจากโครงสร้างพื้นฐานที่มีอยู่
การจัดการ KV cache กลายเป็นคอขวดที่สำคัญสำหรับการ deploy LLM ในระดับ production การใช้หน่วยความจำเพิ่มขึ้นเชิงเส้นตามความยาว sequence และขนาด batch ทำให้หน่วยความจำ GPU หมดอย่างรวดเร็วแม้แต่ GPU ที่มีหน่วยความจำสูงอย่าง H100 และ H200 การเชี่ยวชาญเทคนิคการเพิ่มประสิทธิภาพ cache ช่วยให้สามารถใช้ context ที่ยาวขึ้น batch ที่ใหญ่ขึ้น และ inference ที่คุ้มค่าในระดับขนาดใหญ่
ทำไม KV caching จึงสำคัญ
โมเดล Transformer คำนวณ attention บน token ก่อนหน้าทั้งหมดเมื่อสร้าง token ใหม่แต่ละตัว หากไม่มี caching การสร้าง 1,000 token ต้องคำนวณ attention ใหม่ตั้งแต่ต้น 1,000 ครั้ง—เพิ่มต้นทุนแบบกำลังสองตามความยาว sequence
วิธีแก้ปัญหา KV caching: เก็บ key และ value tensors จาก token ก่อนหน้า นำกลับมาใช้ใหม่สำหรับการคำนวณ attention ในภายหลัง token ใหม่แต่ละตัวคำนวณ attention เทียบกับค่าที่ cache ไว้แทนที่จะสร้างใหม่
ผลกระทบต่อหน่วยความจำ: โมเดล 70B parameter ที่สร้าง 8,192 token ด้วย batch size 32 ต้องการหน่วยความจำ KV cache ประมาณ 40-50GB เพียงอย่างเดียว—มักจะเกิน model weights เสียอีก³
ปัญหาการ scaling: หน่วยความจำ KV cache เพิ่มขึ้นตาม:
Memory = batch_size × seq_length × num_layers × 2 × hidden_dim × precision_bytes
สำหรับ Llama 3.1-70B ด้วย FP16: - Cache ต่อ token: ~2.5MB - Context 8K: ~20GB ต่อคำขอ - Batch ขนาด 32: ~640GB รวม KV cache
PagedAttention: การเพิ่มประสิทธิภาพพื้นฐาน
PagedAttention ของ vLLM ปฏิวัติการจัดการ KV cache โดยถือว่าหน่วยความจำ GPU เหมือนหน่วยความจำเสมือนของระบบปฏิบัติการ:⁴
วิธีการทำงาน
การจัดสรรแบบดั้งเดิม: จองบล็อกหน่วยความจำต่อเนื่องสำหรับความยาว sequence สูงสุดที่เป็นไปได้ context สูงสุด 4K จะจัดสรร cache 4K แม้สำหรับคำขอ 100 token ทำให้สูญเสียหน่วยความจำที่จองไว้ 97.5%
การจัดสรรแบบ paged: แบ่ง KV cache เป็นบล็อกขนาดคงที่ (pages) จัดสรร pages ตามความต้องการเมื่อ sequence เติบโต คืน pages เมื่อ sequence เสร็จสิ้น
การ mapping ตาราง block: เหมือนตาราง page ของ OS PagedAttention รักษาการ mapping ระหว่างตำแหน่ง sequence เชิงตรรกะและตำแหน่งหน่วยความจำทางกายภาพ Sequence เห็นหน่วยความจำต่อเนื่องในขณะที่การจัดเก็บทางกายภาพยังคงไม่ต่อเนื่อง
ผลกระทบต่อประสิทธิภาพ
- การสูญเสียหน่วยความจำ: 60-80% → ต่ำกว่า 4%
- Throughput: ปรับปรุง 2-4 เท่าเทียบกับการจัดสรรแบบดั้งเดิม
- การกระจัดกระจายของหน่วยความจำ: แทบจะหมดไป⁵
การ implement ใน vLLM
from vllm import LLM, SamplingParams
# PagedAttention เปิดใช้งานโดยค่าเริ่มต้น
llm = LLM(
model="meta-llama/Llama-3.1-70B-Instruct",
tensor_parallel_size=4,
gpu_memory_utilization=0.90, # ใช้ 90% ของหน่วยความจำ GPU
max_model_len=32768,
)
vLLM จัดการการจัดสรร page การคืน และการแชร์หน่วยความจำโดยอัตโนมัติโดยไม่ต้องกำหนดค่าเพิ่มเติม
Prefix caching และการแชร์หน่วยความจำ
PagedAttention ช่วยให้สามารถแชร์หน่วยความจำอย่างมีประสิทธิภาพข้ามคำขอที่มี prefix ร่วมกัน:
System prompts ที่แชร์: เมื่อคำขอหลายรายการใช้ system prompts เหมือนกัน physical pages ที่เก็บ token เหล่านั้นจะถูกแชร์แทนที่จะทำซ้ำ
Automatic prefix caching: Automatic Prefix Caching (APC) ของ vLLM ตรวจจับ prefix ร่วมข้ามคำขอและแชร์ KV cache blocks โดยอัตโนมัติ:
llm = LLM(
model="meta-llama/Llama-3.1-8B-Instruct",
enable_prefix_caching=True,
)
ผลกระทบในระดับ production: แอปพลิเคชันที่มี system prompts คงที่หรือ context ซ้ำ (RAG กับเอกสารร่วม ตัวอย่าง few-shot) เห็นการประหยัดหน่วยความจำและลด latency อย่างมาก อัตราการ hit cache 87%+ สามารถทำได้ด้วย prompts ที่มีโครงสร้างดี⁶
การ quantize KV cache
การบีบอัดค่า KV cache ลดความต้องการหน่วยความจำโดยแลกกับการลดความแม่นยำเล็กน้อย:
FP8 KV cache
GPU Hopper และ Blackwell รองรับ FP8 KV cache แบบ native:
# vLLM FP8 KV cache
llm = LLM(
model="meta-llama/Llama-3.1-70B-Instruct",
kv_cache_dtype="fp8",
)
FP8 ลดหน่วยความจำ KV cache ลงครึ่งหนึ่งเทียบกับ FP16 โดยมีผลกระทบต่อคุณภาพน้อยมากสำหรับแอปพลิเคชันส่วนใหญ่ การเพิ่มประสิทธิภาพนี้กลายเป็นสิ่งจำเป็นสำหรับ inference context ยาวที่ cache ครอบงำการใช้หน่วยความจำ
INT4 KV cache
การรองรับ 4-bit KV cache แบบทดลองลดหน่วยความจำได้มากขึ้น:⁷ - การลดหน่วยความจำ: 4 เท่าเทียบกับ FP16 - ผลกระทบต่อคุณภาพ: ขึ้นอยู่กับงาน ต้องประเมิน - เหมาะสำหรับ: แอปพลิเคชัน context ยาวที่มีข้อจำกัดหน่วยความจำ
การเลือก quantization
| Precision | การประหยัดหน่วยความจำ | ผลกระทบต่อคุณภาพ | กรณีการใช้งาน |
|---|---|---|---|
| FP16 | Baseline | ไม่มี | ค่าเริ่มต้น สำคัญต่อคุณภาพ |
| FP8 | 50% | น้อยมาก | Production inference |
| INT8 | 50% | ต่ำ | การ deploy ที่คำนึงถึงต้นทุน |
| INT4 | 75% | ปานกลาง | ข้อจำกัดหน่วยความจำขั้นสุด |
กลยุทธ์การ evict cache
เมื่อแรงกดดันหน่วยความจำเกินความจุที่มี นโยบายการ evict cache กำหนดว่าจะ drop token ใด:
Sliding window attention
รักษาเฉพาะ token ล่าสุดใน cache drop context เก่า:
# Sliding window เชิงแนวคิด
def sliding_window_cache(kv_cache, window_size):
if len(kv_cache) > window_size:
kv_cache = kv_cache[-window_size:]
return kv_cache
เรียบง่ายแต่มีประสิทธิภาพสำหรับแอปพลิเคชันที่ context ล่าสุดสำคัญที่สุด Sliding window เชิงสถาปัตยกรรม (เช่น Mistral) implement สิ่งนี้โดย native
การ evict ตาม attention
ลบ token ที่มี attention scores ต่ำสุด เก็บ context สำคัญ:
PagedEviction (2025): อัลกอริทึมการ evict แบบ block-wise ที่ออกแบบมาสำหรับ PagedAttention ที่ระบุและลบ blocks ที่ไม่สำคัญโดยไม่ต้องแก้ไข CUDA kernels⁸
Entropy-guided caching: จัดสรรงบประมาณ cache ตาม attention entropy ของ layer—layer ที่มี attention patterns กว้างได้รับ cache มากกว่า layer ที่เน้นได้รับน้อยกว่า⁹
Streaming LLM
สำหรับการสร้างความยาวไม่จำกัด Streaming LLM รักษา: - Token "attention sink" เริ่มต้น (4-8 token แรก) - Token ล่าสุดภายใน sliding window - Drop context ตรงกลาง
วิธีการนี้ช่วยให้สามารถสร้างได้ไม่จำกัดในทางทฤษฎีด้วยหน่วยความจำคงที่ แม้ว่าคุณภาพจะลดลงสำหรับงานที่ต้องการ dependencies ระยะยาว
การ offload KV cache
เมื่อหน่วยความจำ GPU ไม่เพียงพอ offload cache ไปยังชั้นจัดเก็บที่ช้ากว่า:
CPU offloading
ย้าย cache ของ sequence ที่ไม่ active ไปยัง system RAM:
# LMCache integration สำหรับ offloading
from lmcache import LMCacheEngine
cache_engine = LMCacheEngine(
backend="cpu",
max_gpu_cache_size="20GB",
cpu_cache_size="100GB",
)
ผลกระทบต่อ latency: การถ่ายโอน CPU-GPU เพิ่ม 10-50ms ต่อการดึง cache เหมาะสำหรับ workloads แบบ batch หรือเมื่อข้อจำกัดหน่วยความจำ GPU ป้องกันการให้บริการเลย
ประสิทธิภาพ: LMCache กับ vLLM ลด latency ลง 3-10 เท่าเทียบกับการคำนวณใหม่โดย caching ใน CPU memory แทนที่จะสร้างใหม่¹⁰
Disk offloading
สำหรับกรณีสุดขั้ว cache ไปยัง NVMe storage: - Latency: 100-500ms ต่อการดึง - กรณีการใช้งาน: Context ยาวมากที่ไม่สามารถทำได้อย่างอื่น - ไม่เหมาะสำหรับแอปพลิเคชันแบบ interactive
Tiered caching
ระบบ production มักจะ implement caching หลายชั้น:
- GPU HBM: Sequence ร้อนที่กำลังสร้างอยู่
- CPU RAM: Sequence อุ่นที่เพิ่ง active
- NVMe SSD: Sequence เย็นสำหรับการใช้ซ้ำที่อาจเกิดขึ้น
นโยบาย promotion และ demotion อัจฉริยะย้าย cache ระหว่างชั้นตามรูปแบบการเข้าถึง
การ routing ที่คำนึงถึง KV cache
Distributed inference ได้ประโยชน์จากการ route คำขอไปยัง pods ที่ถือ cache ที่เกี่ยวข้อง:
llm-d framework
Framework native Kubernetes พร้อม routing ที่คำนึงถึง KV cache:¹¹
# การกำหนดค่า cache routing ของ llm-d
routing:
strategy: kv_cache_aware
cache_hit_weight: 0.8
load_balance_weight: 0.2
ผลลัพธ์ประสิทธิภาพ: - อัตรา cache hit 87% กับ workloads ที่มี prefix มาก - time-to-first-token เร็วขึ้น 88% สำหรับ cache hits ที่อุ่น - ลดการคำนวณซ้ำซ้อนอย่างมากทั่วทั้ง cluster
รูปแบบการ implement
Sticky sessions: Route คำขอจากการสนทนาเดียวกันไปยัง pod เดียวกัน
Prefix hashing: Hash system prompts เพื่อกำหนด pod routing ทำให้มั่นใจว่า prefix cache hits
Load-aware routing: สมดุลระหว่าง cache locality กับการใช้งาน pod เพื่อป้องกัน hotspots
คู่มือการ sizing สำหรับ production
การประมาณหน่วยความจำ
คำนวณความต้องการ KV cache ก่อนการ deploy:
def estimate_kv_cache_memory(
num_layers: int,
hidden_dim: int,
num_kv_heads: int,
head_dim: int,
max_seq_len: int,
max_batch_size: int,
precision_bytes: int = 2, # FP16
) -> float:
"""ประมาณหน่วยความจำ KV cache เป็น GB"""
per_token = num_layers * 2 * num_kv_heads * head_dim * precision_bytes
total = per_token * max_seq_len * max_batch_size
return total / (1024 ** 3)
# ตัวอย่าง Llama 3.1-70B
memory_gb = estimate_kv_cache_memory(
num_layers=80,
hidden_dim=8192,
num_kv_heads=8, # GQA
head_dim=128,
max_seq_len=8192,
max_batch_size=32,
)
print(f"หน่วยความจำ KV cache: {memory_gb:.1f} GB")
การวางแผนความจุ
กฎง่าย: สำรอง 40-60% ของหน่วยความจำ GPU สำหรับ KV cache ส่วนที่เหลือสำหรับ model weights และ activations
ตัวอย่าง H100 80GB: - Model weights (70B FP16): ~140GB → 2x GPU ด้วย tensor parallelism - หน่วยความจำต่อ GPU ที่ใช้ได้สำหรับ cache: ~30-35GB หลังจาก weights และ overhead - Sequence พร้อมกันสูงสุด: ขึ้นอยู่กับความยาว context เฉลี่ย
ลำดับความสำคัญในการเพิ่มประสิทธิภาพ
- เปิดใช้งาน PagedAttention: ค่าเริ่มต้นใน vLLM การเพิ่มประสิทธิภาพหลัก
- เปิดใช้งาน prefix caching: ถ้า workloads มี prefix ร่วม
- Implement FP8 KV cache: เมื่อใช้ GPU Hopper/Blackwell
- เพิ่ม cache-aware routing: ในระดับ cluster กับ distributed inference
- พิจารณา offloading: เฉพาะเมื่อหน่วยความจำ GPU ไม่เพียงพอ
การ monitoring และ observability
ติดตาม KV cache metrics ในระดับ production:
Metrics หลัก: - การใช้งาน cache: เปอร์เซ็นต์ของ cache ที่จัดสรรที่ใช้งานอยู่ - อัตรา cache hit: ประสิทธิภาพของ prefix cache - อัตราการ eviction: ความถี่ของ cache overflow - การกระจัดกระจายของหน่วยความจำ: พื้นที่สูญเสียภายใน blocks ที่จัดสรร
vLLM metrics endpoint:
# Prometheus metrics พร้อมใช้งานที่ /metrics
# kv_cache_usage_percent
# kv_cache_total_blocks
# kv_cache_used_blocks
# prefix_cache_hit_rate
Alerting thresholds: - การใช้งาน cache > 90%: เพิ่มความจุหรือลด batch size - อัตรา hit < 50%: ตรวจสอบการกำหนดค่า prefix caching - อัตราการ eviction สูง: เพิ่มการจัดสรรหน่วยความจำหรือเพิ่มประสิทธิภาพ prompts
องค์กรที่ deploy LLM inference ในระดับ production สามารถใช้ประโยชน์จากความเชี่ยวชาญด้านโครงสร้างพื้นฐานของ Introl สำหรับการวางแผนความจุ GPU และการเพิ่มประสิทธิภาพทั่วการ deploy ระดับโลก
ความจำเป็นของประสิทธิภาพหน่วยความจำ
การเพิ่มประสิทธิภาพ KV cache เป็นหนึ่งในการปรับปรุงที่มีผลกระทบสูงสุดสำหรับการ deploy LLM ในระดับ production PagedAttention เพียงอย่างเดียวให้การปรับปรุง throughput 2-4 เท่า—เทียบเท่ากับการเพิ่มการลงทุน GPU เป็นสองเท่าหรือสี่เท่าโดยไม่มีค่าใช้จ่ายฮาร์ดแวร์เพิ่มเติม
ภูมิทัศน์การเพิ่มประสิทธิภาพยังคงพัฒนาต่อไป FastGen ของ Microsoft แสดงให้เห็นการลดหน่วยความจำ 50% ผ่านการบีบอัดแบบ adaptive Entropy-guided caching จัดสรรงบประมาณอย่างชาญฉลาดข้าม layers Cache-aware routing ช่วยให้ได้การเพิ่มประสิทธิภาพระดับ cluster ที่ก่อนหน้านี้เป็นไปไม่ได้
สำหรับองค์กรที่รัน inference ในระดับขนาดใหญ่ การเพิ่มประสิทธิภาพ KV cache ควรอยู่ในลำดับแรกของการเพิ่มประสิทธิภาพที่ประเมิน เทคนิคต้องการการลงทุนน้อย