KV cache กับ prompt cache ต่างกันอย่างไร สรุปชัดเจน
เข้าใจความแตกต่างระหว่าง KV cache และ prompt cache ในเชิงเทคนิคและค่าใช้จ่าย KV cache คือหน่วยความจำที่ใช้ประมวลผลจริงจนอาจทำให้โมเดลโหลดไม่ขึ้น ส่วน prompt cache คือฟีเจอร์ลดราคา
KV cache กับ prompt cache: คำตอบโดยสรุป
KV cache และ prompt cache ของผู้ให้บริการมีเพียงชื่อที่คล้ายกัน แต่ในทางปฏิบัติแทบไม่มีส่วนใดที่เหมือนกัน KV cache คือหน่วยความจำขณะทำงาน (working memory) สำหรับแต่ละคำขอ (request) โดยจะอยู่ใน RAM หรือ VRAM ของเซิร์ฟเวอร์ตลอดช่วงเวลาของคำขอนั้นๆ และจะมีขนาดเพิ่มขึ้นตามความยาวของบริบท (context length) รวมถึงจำนวนคำขอที่ประมวลผลพร้อมกัน ส่วน prompt caching ของผู้ให้บริการเป็นฟีเจอร์ด้านการเรียกเก็บเงินและลดความหน่วง (latency) โดยส่วนนำของ prompt ที่คงที่ของคุณจะถูกจัดเก็บไว้บนเซิร์ฟเวอร์ของผู้ให้บริการ และจะคิดค่าบริการในราคาพิเศษเมื่อคุณส่ง prompt นั้นซ้ำอีกครั้ง
อย่างหนึ่งคือหน่วยความจำที่คุณซื้อเป็นฮาร์ดแวร์ ส่วนอีกอย่างคือหน่วยความจำที่ผู้อื่นถือครองและเรียกเก็บค่าเช่าจากคุณ
ความแตกต่างในทางปฏิบัติมีความสำคัญมากกว่าคำนิยาม คุณสามารถประสบปัญหา KV cache เต็ม ซึ่งเมื่อเกิดขึ้น โมเดลจะไม่โหลดหรือคำขอจะถูกปฏิเสธ แต่คุณไม่สามารถทำให้ prompt cache เต็มได้ คุณทำได้เพียงแค่เรียกใช้ไม่สำเร็จ (fail to hit) ซึ่งผลที่ตามมาคือคุณเพียงแค่ต้องจ่ายค่าบริการในราคาเต็มตามปกติเท่านั้น
สิ่งที่ KV cache จัดเก็บและเหตุผลที่มีการใช้งาน
Transformer ที่กำลังสร้าง token ลำดับที่ 500 จำเป็นต้องประมวลผล (attend) token ทั้ง 499 ตัวก่อนหน้า สำหรับ token แต่ละตัว ทุกเลเยอร์ต้องการ key vector และ value vector การคำนวณค่าเหล่านี้ใหม่ทั้งหมดสำหรับทุก token ใหม่จะทำให้เวลาที่ใช้ในการสร้างเพิ่มขึ้นเป็นกำลังสองของความยาวลำดับ (square of the length) ดังนั้น runtime จึงจัดเก็บค่าเหล่านี้ไว้แทน พื้นที่จัดเก็บดังกล่าวคือ KV cache (key/value cache)
ข้อมูลนี้เป็นสถานะเฉพาะของแต่ละคำขอ (per-request state) เนื่องจากถูกสร้างขึ้นจากลำดับ token ที่เฉพาะเจาะจงของคำขอนั้นๆ ผู้ใช้สองคนที่ส่ง prompt ต่างกันไม่สามารถใช้ cache ร่วมกันได้ เว้นแต่ runtime จะทำ prefix caching ซึ่งเป็นฟีเจอร์แยกต่างหากที่จะอธิบายในภายหลัง
การให้บริการเกิดขึ้นใน 2 ระยะ ระยะ Prefill จะอ่าน prompt ทั้งหมดของคุณและเติมข้อมูลลงใน cache ซึ่งถูกจำกัดด้วยความสามารถในการคำนวณ (compute) ส่วนระยะ Decode จะสร้าง token ทีละตัวและเพิ่มเข้าไปใน cache ซึ่งถูกจำกัดด้วยแบนด์วิดท์ของหน่วยความจำ (memory bandwidth) การแบ่งระยะเช่นนี้เป็นเหตุผลว่าทำไมการประมวลผล prompt และการสร้าง token จึงแสดงความเร็วที่แตกต่างกันเมื่อคุณ วัดจำนวน token ต่อวินาทีบนเครื่องของคุณเอง
KV cache ใช้หน่วยความจำเท่าใด
ไม่ต้องไปหาตารางจากผู้ผลิต ขนาดของหน่วยความจำคำนวณได้ด้วยสูตรคณิตศาสตร์ที่คุณสามารถทำซ้ำได้กับทุกโมเดล:
bytes per token = 2 * layers * kv_heads * head_dim * bytes per elementเลข 2 หมายถึง key และ value ส่วนตัวเลขอื่นทั้งหมดมาจาก config.json ของโมเดล ซึ่งระบุไว้ในหน้า Hugging Face ของโมเดลนั้น
ลองพิจารณา Llama 3.1 8B โดยที่ config ระบุ num_hidden_layers ไว้ที่ 32 และ num_key_value_heads ไว้ที่ 8 เมื่อนำ hidden_size ขนาด 4096 มากระจายบน attention heads จำนวน 32 หัว จะได้ขนาด head dimension เท่ากับ 128 ที่ความละเอียด f16 แต่ละ element จะใช้พื้นที่ 2 bytes:
2 * 32 * 8 * 128 * 2 = 131072 bytes = 128 KiB per tokenให้นำค่านี้ไปคูณกับ context ที่คุณต้องการใช้งาน จากนั้นคูณด้วยจำนวนคำขอ (requests) ที่ประมวลผลพร้อมกัน
The data behind this chart
[
{
"label": "2k context",
"one_request_gib": 0.25,
"four_requests_gib": 1
},
{
"label": "8k context",
"one_request_gib": 1,
"four_requests_gib": 4
},
{
"label": "32k context",
"one_request_gib": 4,
"four_requests_gib": 16
},
{
"label": "128k context",
"one_request_gib": 16,
"four_requests_gib": 64
}
]ที่ context ขนาด 8k ตัว cache จะใช้พื้นที่ 1 GiB และที่ 32k จะใช้พื้นที่ 4 GiB ซึ่งอยู่ในระดับเดียวกับขนาดของ weights แบบ 4-bit เอง สำหรับ context สูงสุดของโมเดลที่ 128k จะใช้พื้นที่ 16 GiB ต่อหนึ่งคำขอ และ 64 GiB หากมีสี่คำขอที่ใช้งานเต็มพื้นที่ดังกล่าว โดยที่ขนาดของ weights ไม่ได้เปลี่ยนแปลง มีเพียงแค่ขนาดของ cache เท่านั้นที่เปลี่ยนไป
Grouped query attention (GQA) มีบทบาทสำคัญอย่างมากต่อตัวเลขนี้ Llama 3.1 8B มี key/value heads จำนวน 8 หัวที่รองรับ query heads 32 หัว ดังนั้น query heads สี่หัวจึงใช้ key/value pair ร่วมกันหนึ่งชุด โมเดลที่มีค่า num_key_value_heads เท่ากับ num_attention_heads จะใช้พื้นที่ cache มากกว่าถึงสี่เท่าแม้จะมีจำนวน parameter เท่ากัน โปรดตรวจสอบฟิลด์นั้นก่อนที่คุณจะสรุปว่าโมเดลขนาด 8B สองโมเดลจะมีต้นทุนในการให้บริการเท่ากัน
เหตุใดโมเดลที่เคยรันที่ 2k ถึงโหลดไม่ขึ้นเมื่อปรับเป็น 32k
เนื่องจาก runtime จะจอง KV cache ไว้ตั้งแต่ตอนโหลดโมเดล โดยขนาดจะอ้างอิงตาม context length ที่คุณตั้งค่าไว้ ไม่ใช่ตามขนาดของ prompt ที่คุณส่งจริง ค่าเริ่มต้นของ context window ใน Ollama คือ 4096 tokens หากคุณปรับเพิ่มเป็น 32k เท่ากับว่าคุณได้ร้องขอการจัดสรรหน่วยความจำเพิ่มขึ้นอีก 4 GiB ก่อนที่จะเริ่มประมวลผลแม้แต่ token เดียว
OLLAMA_CONTEXT_LENGTH=32768 ollama serveการตั้งค่าแบบเดียวกันต่อเซสชัน จาก interactive prompt:
ollama run llama3.1:8b
/set parameter num_ctx 32768ลักษณะความล้มเหลวจะแตกต่างกันไปตามแต่ละ stack โดย vLLM จะตรวจสอบการคำนวณตั้งแต่ตอนเริ่มต้นและปฏิเสธที่จะรัน:
ValueError: The model's max seq len (131072) is larger than the maximum number of tokens that can be stored in KV cache (78336). Try increasing `gpu_memory_utilization` or decreasing `max_model_len` when initializing the engine.บน VPS ที่ใช้เฉพาะ CPU จะไม่มีการตรวจสอบดังกล่าว เนื่องจากเป็นการจัดสรรหน่วยความจำระบบปกติ แต่ kernel จะใช้ out-of-memory killer เข้ามาจัดการ process แทน และทิ้งร่องรอยไว้ใน kernel ring buffer:
dmesg -T | grep -i "killed process"หากพบชื่อ process ของคุณปรากฏในบรรทัดดังกล่าว หมายความว่าเซิร์ฟเวอร์มีการสัญญาว่าจะจัดสรรหน่วยความจำให้มากกว่าที่มีอยู่จริง วิธีแก้ไขคือการลดขนาด context ลง ไม่ใช่การเพิ่มขนาด swap file เพราะหาก KV cache ถูกย้ายไปไว้บนดิสก์ ระบบจะต้องอ่านข้อมูลทุกครั้งที่มีการสร้าง token ใหม่ ซึ่งจะทำให้ความเร็วในการสร้างช้าลงจนไม่สามารถใช้งานได้จริง การเลือกตัวเลขที่เหมาะสมได้อธิบายไว้ใน คู่มือการตั้งค่า num_ctx และ context length ใน Ollama ของเรา
ผลกระทบของ concurrency ต่อจำนวนตัวเลข
คำขอที่กำลังประมวลผลแต่ละรายการจะมี KV cache เป็นของตนเอง นี่คือจุดที่แผนการจัดสรรทรัพยากรส่วนใหญ่มักมองข้าม ผู้ใช้งาน 4 คนที่ถือ context ขนาด 32k แต่ละคน ต้องการหน่วยความจำรวมกัน 16 GiB นอกเหนือไปจากขนาดของ weights
Runtime แต่ละตัวมีความเข้มงวดในเรื่องนี้ต่างกัน Ollama และ llama.cpp จะจอง context ตามที่คุณระบุไว้ตั้งแต่ตอนโหลดโมเดล ดังนั้นหน่วยความจำจะถูกจองไว้เสมอไม่ว่าจะมีการใช้งานจริงหรือไม่ก็ตาม ในขณะที่ vLLM จะแบ่งหน่วยความจำออกเป็นบล็อกขนาดคงที่และจัดสรรให้ตามการขยายตัวของแต่ละคำขอ ดังนั้นคำขอขนาด 500 token จะใช้พื้นที่เพียงเท่ากับ 500 token เท่านั้น ไม่ว่าจะเป็นวิธีใดก็ตาม พื้นที่หน่วยความจำมีจำกัด และเมื่อเต็มแล้ว คำขอใหม่จะต้องเข้าคิวแทนที่จะได้ประมวลผลทันที ผลกระทบของการเข้าคิวที่มีต่อเวลาในการตอบสนองได้อธิบายไว้ใน จำนวนผู้ใช้งานพร้อมกันที่ LLM แบบ self-hosted รองรับได้
สี่วิธีในการลดขนาด KV cache
- ลดความยาวของ context วิธีนี้เป็นปัจจัยที่มีผลมากที่สุดและมักจะมีต้นทุนต่ำที่สุด งานประเภทแชทส่วนใหญ่ไม่เคยใช้งาน context ถึง 32k
- ทำ Quantization ให้กับตัว cache เอง ค่าเริ่มต้นของ
OLLAMA_KV_CACHE_TYPEใน Ollama คือf16ซึ่งรองรับq8_0ที่ใช้หน่วยความจำลดลงประมาณครึ่งหนึ่ง และq4_0ที่ใช้หน่วยความจำลดลงเหลือประมาณหนึ่งในสี่ ส่วนค่าที่เทียบเท่ากันใน llama.cpp คือ-ctk q8_0และ-ctv q8_0 - เลือกโมเดลที่มีจำนวน key/value heads หรือจำนวนเลเยอร์น้อยลง โปรดอ่าน
config.jsonก่อนที่คุณจะดาวน์โหลดไฟล์ weights ขนาด 40 GB - ให้บริการคำขอจำนวนน้อยลงในคราวเดียวและนำคำขอที่เหลือเข้าคิวไว้
ที่ q4_0 ตัวเลขของ Llama 3.1 8B จะลดลงจาก 128 KiB ต่อ token เหลือประมาณ 32 KiB ดังนั้น context ขนาด 32k จึงใช้หน่วยความจำประมาณ 1 GiB แทนที่จะเป็น 4 GiB การประหยัดหน่วยความจำนี้มีข้อแลกเปลี่ยน เนื่องจาก key และ value จะถูกจัดเก็บด้วยความละเอียดที่ต่ำลง ดังนั้นควรเปรียบเทียบผลลัพธ์จาก prompt ของคุณเองก่อนที่จะตัดสินใจใช้งานจริง
สิ่งที่ Provider prompt caching มอบให้จริง
Provider prompt caching เป็นผลิตภัณฑ์ที่แตกต่างและมีหน่วยการคิดราคาที่ต่างออกไป คุณทำเครื่องหมายที่ prefix ที่คงที่ จากนั้นผู้ให้บริการจะจัดเก็บไว้ และการเรียกใช้งานในภายหลังที่ใช้ prefix เดิมซ้ำจะถูกคิดค่าบริการในอัตราที่ลดลงแทนที่จะเป็นราคาเต็มของ input
ตัวคูณที่ Anthropic ประกาศไว้ ณ เดือนสิงหาคม 2026: การเขียน cache ระยะเวลา 5 นาทีมีค่าใช้จ่าย 1.25 เท่าของราคา base input token การเขียนระยะเวลา 1 ชั่วโมงมีค่าใช้จ่าย 2 เท่า และการอ่าน cache มีค่าใช้จ่าย 0.1 เท่า เมื่อนำ system prompt ขนาด 20,000 token มาคำนวณตามตัวเลขเหล่านี้ รูปแบบของความคุ้มค่าจะปรากฏชัดเจน
The data behind this chart
[
{
"label": "No caching, every call",
"billed_token_equivalents": "20,000"
},
{
"label": "First call, 5 minute cache write",
"billed_token_equivalents": "25,000"
},
{
"label": "First call, 1 hour cache write",
"billed_token_equivalents": "40,000"
},
{
"label": "Every later call, cache hit",
"billed_token_equivalents": "2,000"
}
]ลองพิจารณาในเชิงคณิตศาสตร์ ค่าพรีเมียมสำหรับการเขียนแบบ 5 นาทีจะเท่ากับ 25,000 token ในการเรียกครั้งแรก: 25,000 เทียบกับ 20,000 หากส่งโดยไม่ใช้ cache การเรียกครั้งถัดไปทั้งหมดภายในช่วงเวลาดังกล่าวจะถูกคิดค่าบริการเพียง 2,000 แทนที่จะเป็น 20,000 ซึ่งประหยัดไปได้ 18,000 token ดังนั้น cache แบบ 5 นาทีจะเริ่มคุ้มทุนตั้งแต่การเรียกครั้งที่สองเป็นต้นไป
cache แบบ 1 ชั่วโมงเป็นการเดิมพันที่ต่างออกไป โดยจะคิดค่าบริการ 40,000 ในการเขียน ซึ่งเป็นค่าพรีเมียมถึง 20,000 token ดังนั้นจึงต้องมีการเรียกใช้งานซ้ำสองครั้งภายในหนึ่งชั่วโมงจึงจะเริ่มคุ้มทุน นี่เป็นคำถามเกี่ยวกับรูปแบบ traffic ของคุณ ไม่ใช่เกี่ยวกับตัวโมเดล การคำนวณฉบับเต็มรวมถึงวิธีการเลือกช่วงเวลา สามารถดูได้ที่ การคำนวณจุดคุ้มทุนสำหรับ Claude prompt caching
มีรายละเอียดสองประการที่ตัดสินว่าคุณจะใช้งาน cache ได้หรือไม่ ประการแรก prefix ที่มีความยาวต่ำกว่าเกณฑ์ขั้นต่ำของโมเดลจะไม่ถูกเก็บใน cache โดยไม่มีการแจ้งเตือน: ณ เดือนสิงหาคม 2026 เกณฑ์ขั้นต่ำที่ระบุไว้คือ 512 tokens สำหรับ Claude Opus 5 และ 1,024 tokens สำหรับ Claude Sonnet 5 หากคำขอสั้นกว่านั้น ระบบจะประมวลผลตามปกติโดยไม่มีข้อผิดพลาดส่งกลับมา ประการที่สอง อายุการใช้งานจะถูกนับตั้งแต่เริ่มคำขอที่เขียนหรืออ่านรายการนั้น และการอ่านทุกครั้งจะเป็นการรีเฟรชอายุ cache โดยไม่มีค่าใช้จ่ายเพิ่มเติม ดังนั้น endpoint ที่มีการใช้งานหนาแน่นจะรักษา cache แบบ 5 นาทีไว้ได้ตลอดไป ในขณะที่ endpoint ที่ถูกเรียกใช้งานทุกๆ สิบนาทีจะต้องจ่ายค่าพรีเมียมการเขียนทุกครั้งและไม่ได้รับประโยชน์ใดๆ
ให้ตรวจสอบที่ response แทนการคาดเดา วัตถุ usage จะรายงานค่า cache_creation_input_tokens และ cache_read_input_tokens หากจำนวนการอ่านเป็นศูนย์ในการเรียกใช้งานทุกครั้ง นั่นหมายความว่าคุณกำลังจ่ายเงินเพื่อเขียนข้อมูลแต่ไม่ได้รับผลตอบแทนใดๆ กลับมา
จุดที่แคชทั้งสองมาบรรจบกัน
System prompt ที่ยาวคือจุดที่ทั้งสองส่วนมาพบกัน และมันจะคิดค่าใช้จ่ายกับคุณทั้งสองฝั่งพร้อมกัน
ในฝั่ง Local นั้น system prompt ขนาด 20,000 token จะใช้พื้นที่ KV cache ประมาณ 2.4 GiB บนเซิร์ฟเวอร์ Llama 3.1 8B ที่ความละเอียด f16 และจะถูกใช้แยกกันสำหรับทุกคำขอที่ทำงานพร้อมกัน ในฝั่ง Remote นั้น prefix เดียวกันนี้จะมีค่าใช้จ่ายในการเขียนแคชหนึ่งครั้ง และจากนั้นจะคิดค่าใช้จ่าย 0.1 เท่าของ input ในการเรียกใช้งานครั้งถัดไป ต้นทุนฝั่ง Local จะแปรผันตามจำนวนผู้ใช้งานของคุณ ส่วนต้นทุนฝั่ง Remote จะแปรผันตามปริมาณ traffic และจะถูกรีเซ็ตในช่วงเวลาที่คุณไม่มีการใช้งาน
มีฟีเจอร์หนึ่งในฝั่ง Local ที่ดูคล้ายกับ prompt caching ของผู้ให้บริการและมักถูกเข้าใจผิดอยู่บ่อยครั้ง นั่นคือ prefix caching เอกสารของ vLLM อธิบายว่า automatic prefix caching คือการแคช "KV cache ของคำถามที่มีอยู่ เพื่อให้คำถามใหม่สามารถนำ KV cache กลับมาใช้ใหม่ได้โดยตรงหากมี prefix เดียวกันกับคำถามที่มีอยู่เดิม" เซิร์ฟเวอร์ llama.cpp จะเก็บ prompt cache แยกตาม slot เป็นค่าเริ่มต้น และ --cache-reuse N จะเป็นตัวกำหนดขนาด chunk เล็กที่สุดที่จะพยายามนำกลับมาใช้ใหม่
สิ่งที่ prefix caching ช่วยประหยัดได้คือการคำนวณในช่วง prefill system prompt ขนาด 20,000 token ของคุณจะถูกประมวลผลเพียงครั้งเดียวแทนที่จะทำในทุกคำขอ ซึ่งช่วยลดเวลาในการรอ token แรกได้อย่างมาก ใน vLLM บล็อกที่แชร์กันจะถูกนำกลับมาใช้ใหม่แทนที่จะทำซ้ำ ทำให้การใช้หน่วยความจำมีประสิทธิภาพมากขึ้นด้วย สิ่งที่มันไม่เคยทำคือการลดขนาดแคชที่คุณต้องสำรองไว้สำหรับ token ที่กำลังใช้งานอยู่ การคงน้ำหนัก (weights) ไว้ในหน่วยความจำระหว่างคำขอเป็นอีกกลไกหนึ่งที่เกี่ยวข้องแต่แยกจากกัน ซึ่งครอบคลุมอยู่ใน การคงโมเดล Ollama ไว้ในหน่วยความจำระหว่างคำขอ
สิ่งที่ควรวัดบนเซิร์ฟเวอร์ของคุณ
ให้โหลดโมเดลที่บริบท (context) เป้าหมายของคุณ จากนั้นอ่านค่าตัวเลขจริงแทนการเชื่อตัวเลขประมาณการ
ollama ps
nvidia-smi --query-gpu=memory.used,memory.total --format=csv
free -gollama ps จะแสดงรายการโมเดลที่โหลดอยู่พร้อมขนาด และระบุว่าโมเดลนั้นทำงานบน GPU หรือ CPU หากคุณคาดหวังว่าโมเดลจะอยู่ใน GPU ทั้งหมดแต่กลับรายงานว่ามีการแบ่งไปใช้ CPU แสดงว่า KV cache ได้ผลักส่วนหนึ่งของโมเดลออกมา ซึ่งจะทำให้ความเร็วในการสร้างข้อความลดลงตามไปด้วย nvidia-smi จะแสดงตัวเลข VRAM ที่แท้จริง และ free -g จะทำหน้าที่เดียวกันบน VPS ที่ใช้เฉพาะ CPU ให้ค่อยๆ เพิ่มค่าบริบททีละขั้น โหลดโมเดลใหม่ แล้วสังเกตการเปลี่ยนแปลงของตัวเลข ค่าที่คุณคำนวณได้ควรใกล้เคียงกับตัวเลขที่รายงาน หากไม่ตรงกัน ส่วนต่างที่เกิดขึ้นมักมาจากบัฟเฟอร์การคำนวณของรันไทม์เอง ไม่ใช่ข้อผิดพลาดของสูตรคำนวณ
หากตัวเลขเหล่านั้นทำให้คุณต้องพิจารณาฮาร์ดแวร์ที่คุณไม่ต้องการเช่าใช้งาน คุณสามารถดูการเปรียบเทียบระหว่างการเช่าฮาร์ดแวร์กับการจ่ายเงินตามจำนวนโทเค็นได้ที่ การเปรียบเทียบ GPU VPS กับ API tokens
FAQ
KV cache เหมือนกับ prompt caching หรือไม่?
ไม่เหมือนกัน KV cache คือหน่วยความจำต่อหนึ่งคำขอ (per-request) ภายในกระบวนการทำงานของเซิร์ฟเวอร์ ซึ่งทำหน้าที่เก็บเวกเตอร์ key และ value สำหรับทุกโทเค็นในบริบทปัจจุบัน ข้อมูลนี้จะอยู่ใน RAM หรือ VRAM และจะถูกล้างออกเมื่อคำขอนั้นสิ้นสุดลง ส่วน prompt caching ของผู้ให้บริการเป็นฟีเจอร์ด้านการเรียกเก็บเงินที่จัดเก็บส่วนนำของ prompt ที่คงที่ไว้บนโครงสร้างพื้นฐานของผู้ให้บริการ และคิดค่าบริการในอัตราที่ลดลงเมื่อคุณส่ง prompt นั้นซ้ำ การที่ KV cache เต็มจะทำให้โมเดลไม่สามารถโหลดได้ แต่การไม่มี prompt cache จะส่งผลเพียงแค่ค่าใช้จ่ายที่สูงขึ้นและเวลาในการสร้างโทเค็นแรก (time to first token) ที่นานขึ้นเท่านั้น
ทำไมโมเดลของฉันโหลดที่บริบท 2k ได้ แต่ล้มเหลวที่ 32k?
เพราะรันไทม์จะจองพื้นที่ KV cache ทั้งหมดตั้งแต่ตอนโหลด โดยกำหนดขนาดตามความยาวบริบทที่คุณตั้งค่าไว้ ไม่ใช่ตามขนาดของ prompt ที่คุณส่งจริง สำหรับ Llama 3.1 8B ที่ความละเอียด f16 แคชจะใช้พื้นที่ 128 KiB ต่อหนึ่งโทเค็น ดังนั้นบริบท 2k จะใช้พื้นที่ 0.25 GiB และ 32k จะใช้พื้นที่ 4 GiB ในขณะที่น้ำหนักของโมเดล (weights) สามารถโหลดได้ทั้งสองกรณี แต่ปัญหาเกิดจากการจองพื้นที่หน่วยความจำไม่สำเร็จ vLLM จะรายงานข้อผิดพลาดนี้เป็น ValueError โดยระบุจำนวนโทเค็นสูงสุดที่สามารถจัดเก็บได้ และแนะนำให้เพิ่มค่า gpu_memory_utilization หรือลดค่า max_model_len ลง ในกรณีที่ใช้ CPU เพียงอย่างเดียว เคอร์เนลจะสั่ง kill กระบวนการทำงานด้วย OOM killer ซึ่งคุณสามารถตรวจสอบได้ด้วย dmesg -T | grep -i "killed process"
ฉันจะคำนวณขนาด KV cache สำหรับโมเดลของฉันได้อย่างไร?
ให้นำ 2 คูณด้วยจำนวนเลเยอร์, จำนวนหัว key/value, มิติของหัว (head dimension) และจำนวนไบต์ต่อหนึ่งอิลิเมนต์ ผลลัพธ์ที่ได้คือจำนวนไบต์ต่อหนึ่งโทเค็น จากนั้นให้นำไปคูณกับความยาวบริบทและจำนวนคำขอที่ทำงานพร้อมกัน คุณสามารถอ่านค่าจำนวนเลเยอร์และจำนวนหัวได้จากไฟล์ config.json ของโมเดล สำหรับ f16 หรือ bf16 ให้ใช้ 2 ไบต์ต่อหนึ่งอิลิเมนต์ หากเป็นแคชแบบ q8_0 จะใช้พื้นที่ประมาณครึ่งหนึ่ง และถ้าเป็น q4_0 จะใช้พื้นที่ประมาณหนึ่งในสี่
prompt caching ช่วยลดหน่วยความจำที่เซิร์ฟเวอร์ของฉันต้องใช้หรือไม่?
prompt caching ของผู้ให้บริการไม่ได้ช่วยลดภาระฮาร์ดแวร์ของคุณ เพราะการจัดเก็บข้อมูลเกิดขึ้นที่ฝั่งผู้ให้บริการ สิ่งที่เทียบเท่ากันในระดับโลคัลคือ prefix caching ซึ่งมีให้ใช้งานทั้งใน vLLM และเซิร์ฟเวอร์ llama.cpp โดยจะนำเวกเตอร์ key และ value ที่คำนวณไว้แล้วสำหรับส่วนนำที่เหมือนกันกลับมาใช้ใหม่ ซึ่งช่วยลดการคำนวณช่วง prefill และลดเวลาในการสร้างโทเค็นแรก ใน vLLM บล็อกที่ใช้ร่วมกันจะถูกนำกลับมาใช้ใหม่แทนการทำซ้ำ จึงช่วยประหยัดหน่วยความจำได้ด้วย แต่ฟีเจอร์ทั้งสองนี้ไม่ได้ลดขนาดแคชที่จำเป็นสำหรับโทเค็นที่กำลังประมวลผลอยู่ ดังนั้นการคำนวณบริบทและความพร้อมกัน (concurrency) ของคุณยังคงเป็นตัวกำหนดความต้องการหน่วยความจำขั้นต่ำอยู่ดี
คุ้มค่าหรือไม่ที่จะแคช prompt ที่ฉันส่งเพียงครั้งเดียว?
ไม่คุ้มค่า การเขียนข้อมูลลงแคชมีค่าใช้จ่ายสูงกว่าการส่งอินพุตปกติ โดยคิดเป็น 1.25 เท่าของอัตราพื้นฐานสำหรับตัวเลือก 5 นาที (ข้อมูล ณ เดือนสิงหาคม 2026) ดังนั้นส่วนนำที่คุณไม่เคยส่งซ้ำภายในช่วงเวลาดังกล่าวจะทำให้คุณเสียค่าใช้จ่ายโดยเปล่าประโยชน์ การแคชจะคุ้มค่าเมื่อมีการใช้ส่วนนำเดิมซ้ำ เช่น system prompt ที่ยาวหรือเอกสารที่คุณต้องการถามคำถามหลายครั้ง ให้ตรวจสอบค่า cache_read_input_tokens ในการตอบกลับของ API เพื่อยืนยันว่าคุณได้รับผลลัพธ์จากการแคช (hits) แทนที่จะต้องเสียค่าใช้จ่ายในการเขียนข้อมูลใหม่ทุกครั้ง