تفاوت KV cache و prompt cache در مدلهای زبانی
تفاوت کلیدی KV cache و prompt cache را درک کنید. KV cache حافظه RAM سرور شما برای هر درخواست است، در حالی که prompt caching قابلیتی برای کاهش هزینه و تاخیر در API است.
تفاوت KV cache و prompt cache: پاسخ کوتاه
KV cache و prompt cache ارائهدهنده، تنها در یک کلمه مشترک هستند و تقریباً هیچ شباهت دیگری ندارند. KV cache حافظهٔ کاری برای هر درخواست است. این حافظه در طول عمر یک درخواست در RAM یا VRAM سرور شما قرار میگیرد و با افزایش طول context و تعداد درخواستهای همزمان، رشد میکند. Prompt caching ارائهدهنده، یک قابلیت مربوط به هزینهها و تأخیر (latency) است. یک پیشوند ثابت از prompt شما روی سرورهای ارائهدهنده ذخیره میشود و هنگام ارسال مجدد، با تخفیف محاسبه میگردد.
یکی حافظهای است که به عنوان سختافزار خریداری میکنید. دیگری حافظهای است که شخص دیگری نگهداری میکند و بابت آن از شما اجاره میگیرد.
تفاوت عملی، اهمیت بیشتری نسبت به تعریف آن دارد. ممکن است ظرفیت KV cache شما تمام شود؛ در این صورت مدل بارگذاری نمیشود یا درخواست رد خواهد شد. ظرفیت prompt cache هرگز تمام نمیشود. تنها ممکن است در استفاده از آن موفق نباشید (cache hit رخ ندهد) که در این حالت، به سادگی هزینه کامل را پرداخت خواهید کرد.
محتوای KV cache و دلیل وجود آن
یک مدل transformer که در حال تولید توکن شماره 500 است، باید به تمام 499 توکن پیش از خود توجه (attend) کند. برای هر یک از آن توکنها، هر لایه به یک بردار کلید (key vector) و یک بردار مقدار (value vector) نیاز دارد. محاسبهٔ مجدد همهٔ آنها برای هر توکن جدید باعث میشود که زمان تولید با مربع طول توالی رشد کند؛ بنابراین runtime آنها را ذخیره میکند. این فضای ذخیرهسازی همان KV cache (کش کلید/مقدار) است.
این کش یک وضعیت مختص به هر درخواست (per-request state) است، زیرا دقیقاً از توالی توکنهای همان درخواست ساخته شده است. دو کاربر که promptهای متفاوتی میفرستند نمیتوانند از آن به صورت مشترک استفاده کنند، مگر اینکه runtime از قابلیت prefix caching استفاده کند که یک ویژگی مجزا است و در ادامه توضیح داده شده است.
سرویسدهی در دو مرحله انجام میشود. مرحلهٔ Prefill کل prompt شما را میخواند و کش را پر میکند و محدود به توان پردازشی (compute) است. مرحلهٔ Decode هر بار یک توکن تولید کرده و به کش اضافه میکند و محدود به پهنای باند حافظه (memory bandwidth) است. همین تفکیک باعث میشود که سرعت پردازش prompt و سرعت تولید توکن هنگام اندازهگیری توکن بر ثانیه روی سیستم خودتان متفاوت گزارش شود.
حافظه 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 را در نظر بگیرید. فایل پیکربندی آن num_hidden_layers را 32 و num_key_value_heads را 8 ذکر کرده است. hidden_size برابر با 4096 که بین 32 هد توجه (attention head) تقسیم شده، ابعاد هر هد را 128 به دست میدهد. در حالت f16، هر عنصر 2 بایت است:
2 * 32 * 8 * 128 * 2 = 131072 bytes = 128 KiB per tokenاین مقدار را در context مورد نظر خود و سپس در تعداد درخواستهایی که همزمان اجرا میکنید، ضرب کنید.
دادههای پشت این نمودار
[
{
"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 گیگابایت است. در 32k این مقدار 4 گیگابایت است که در همان محدوده وزنهای 4-bit قرار دارد. در حداکثر context مدل یعنی 128k، این مقدار برای یک درخواست 16 گیگابایت و اگر چهار درخواست همزمان آن را پر کنند، 64 گیگابایت خواهد بود. وزنها تغییر نکردند، فقط حافظه cache تغییر کرد.
قابلیت Grouped query attention (GQA) در این عدد نقش مهمی دارد. مدل Llama 3.1 8B دارای 8 هد key/value است که به 32 هد query سرویس میدهند، بنابراین چهار هد query از یک جفت key/value ذخیرهشده مشترک استفاده میکنند. مدلی که num_key_value_heads آن با num_attention_heads برابر باشد، در تعداد پارامتر یکسان، چهار برابر بیشتر از حافظه cache استفاده میکند. پیش از آنکه فرض کنید هزینه سرویسدهی دو مدل 8B یکسان است، حتماً آن فیلد را بررسی کنید.
چرا مدلی که در 2k اجرا میشد، در 32k بارگذاری نمیشود
دلیل این است که runtime هنگام بارگذاری مدل، حافظه KV cache را بر اساس طول متنی که پیکربندی کردهاید رزرو میکند، نه بر اساس پرامپتی که واقعاً ارسال میکنید. مقدار پیشفرض Ollama دیگر یک عدد ثابت نیست. از اکتبر 2026، مستندات طول متن (context length) آن را بر اساس VRAM تعیین میکند: 4k برای سیستمهای با کمتر از 24 GiB حافظه، 32k برای سیستمهای بین 24 تا 48 GiB، و 256k برای 48 GiB یا بیشتر؛ در حالی که مدلهای ابری بهطور پیشفرض از حداکثر ظرفیت خود استفاده میکنند. یک VPS فقط با CPU یا یک GPU کوچک، کار را با 4k شروع میکند. اگر آن را به 32k افزایش دهید، پیش از آنکه حتی یک توکن دریافت شود، 4 GiB حافظه اضافی درخواست کردهاید. روی یک کارت 24 GiB، مقدار 32k از قبل پیشفرض است، بنابراین این رزرو حافظه چه شما بخواهید و چه نخواهید، انجام میشود.
OLLAMA_CONTEXT_LENGTH=32768 ollama serveهمین تنظیم برای هر نشست (session)، از طریق پرامپت تعاملی:
ollama run llama3.1:8b
/set parameter num_ctx 32768خطا در هر پشته (stack) متفاوت ظاهر میشود. vLLM محاسبات را در زمان شروع بررسی کرده و از اجرا امتناع میکند:
ValueError: To serve at least one request with the model's max seq len (131072), (16.0 GiB KV cache is needed, which is larger than the available KV cache memory (9.56 GiB). Based on the available memory, the estimated maximum model length is 78336. Try increasing `gpu_memory_utilization` (which also controls CPU memory on the CPU backend) or decreasing `max_model_len` when initializing the engine. See https://docs.vllm.ai/en/latest/configuration/conserving_memory/ for more details.در یک VPS فقط با CPU که Ollama یا llama.cpp را اجرا میکند، تخصیص حافظه از RAM معمولی سیستم انجام میشود و ممکن است کمبود آن در زمان شروع شناسایی نشود. در عوض، مکانیزم out-of-memory killer در هسته (kernel)، پردازش را متوقف میکند و شواهد آن را در kernel ring buffer باقی میگذارد:
dmesg -T | grep -i "killed process"خطی که نام پردازش سرویسدهنده شما را ذکر میکند، به این معناست که سیستم بیش از حافظه موجود، قول تخصیص داده است. راهحل، استفاده از طول متن کوچکتر است، نه یک فایل swap بزرگتر: اگر KV cache روی دیسک صفحهبندی (page) شود، برای هر توکن تولید شده خوانده میشود و سرعت تولید به قدری کاهش مییابد که عملاً غیرقابل استفاده میشود. انتخاب یک عدد منطقی در راهنمای ما درباره num_ctx و طول متن در Ollama توضیح داده شده است.
تأثیر همروندی بر تعداد
هر درخواست در حال پردازش، KV cache اختصاصی خود را حمل میکند. این نکتهای است که اکثر برنامهریزیهای ظرفیت از آن غافل میشوند. چهار کاربر که هر کدام 32k متن (context) در اختیار دارند، علاوه بر وزنهای مدل، به 16 گیگابایت حافظه نیاز دارند.
محیطهای اجرا (Runtimes) در میزان سختگیری نسبت به این موضوع متفاوت هستند. Ollama و llama.cpp فضای متنی (context) درخواستی شما را هنگام بارگذاری مدل رزرو میکنند؛ بنابراین حافظه چه استفاده شود و چه نشود، اشغال شده است. vLLM این استخر حافظه را به بلوکهایی با اندازه ثابت تقسیم کرده و با رشد هر درخواست، آنها را تخصیص میدهد؛ بنابراین یک درخواست 500 توکنی فقط به اندازه 500 توکن فضا اشغال میکند. در هر دو حالت، استخر حافظه محدود است و پس از پر شدن، درخواستهای جدید به جای اجرا شدن، در صف قرار میگیرند. تأثیر این صف بر زمان پاسخدهی در تعداد کاربرانی که یک LLM خودمیزبان میتواند همزمان سرویسدهی کند بررسی شده است.
چهار روش برای کاهش حجم KV cache
- کاهش طول context. این بزرگترین اهرم است و معمولاً کمهزینهترین روش محسوب میشود. اکثر بارهای کاری چت هرگز به نزدیکی 32k نمیرسند.
- کوانتایز کردن (Quantise) خودِ cache. مقدار پیشفرض
OLLAMA_KV_CACHE_TYPEدر Ollama برابر باf16است و ازq8_0نیز پشتیبانی میکند که حدود نصف حافظه را مصرف میکند، و همچنینq4_0که حدود یکچهارم حافظه را اشغال میکند. معادلهای این موارد در llama.cpp عبارتند از-ctk q8_0و-ctv q8_0. - انتخاب مدلی با تعداد سر (head) یا لایههای key/value کمتر. پیش از دانلود 40 GB وزن (weights)،
config.jsonرا مطالعه کنید. - سرویسدهی به تعداد درخواستهای همزمان کمتر و قرار دادن بقیه در صف.
در q4_0، مقدار حافظه برای Llama 3.1 8B از 128 KiB به ازای هر توکن به حدود 32 KiB کاهش مییابد؛ بنابراین 32k از context به جای 4 GiB، حدود 1 GiB هزینه دارد. این صرفهجویی بدون هزینه نیست. کلیدها و مقادیر با دقت کمتری ذخیره میشوند، بنابراین پیش از نهایی کردن، خروجی را با promptهای خودتان مقایسه کنید.
مزایای واقعی کش کردن پرامپت در سمت ارائهدهنده
کش کردن پرامپت (Prompt Caching) در سمت ارائهدهنده، محصولی متفاوت با واحد محاسباتی متفاوت است. شما یک پیشوند (prefix) ثابت را مشخص میکنید، ارائهدهنده آن را ذخیره میکند و در فراخوانیهای بعدی که دقیقاً همان پیشوند را تکرار میکنند، هزینه به جای قیمت کامل ورودی، با نرخ کاهشیافته محاسبه میشود.
ضرایب منتشرشده توسط Anthropic تا اکتبر 2026 به این شرح است: هزینه نوشتن در کش با ماندگاری 5 دقیقه، 1.25 برابر قیمت پایه توکن ورودی است. هزینه نوشتن با ماندگاری 1 ساعت، 2 برابر است و هزینه خواندن از کش در اکثر مدلها 0.1 برابر قیمت پایه است. جدیدترین مدلها حتی با هزینه کمتری میخوانند: 0.05 برابر در Claude Opus 5.5 و Sonnet 5.5، و 0.025 برابر در Claude Fable 5.1 و Mythos 5.1. اگر یک پرامپت سیستمی 20,000 توکنی را با این اعداد استاندارد در نظر بگیرید، صرفه اقتصادی آن کاملاً مشخص میشود.
دادههای پشت این نمودار
[
{
"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 دقیقهای در اولین فراخوانی معادل 5,000 توکن است: 25,000 در مقابل 20,000 برای ارسال بدون کش. هر فراخوانی بعدی در بازه زمانی، به جای 20,000، هزینه 2,000 را در بر دارد که به معنای 18,000 توکن صرفهجویی است. بنابراین، کش 5 دقیقهای از فراخوانی دوم به بعد سودآور میشود.
کش 1 ساعته یک شرطبندی متفاوت است. این کش در زمان نوشتن هزینه 40,000 را دریافت میکند که معادل 20,000 توکن هزینه اضافی است، بنابراین برای سودآور شدن به دو فراخوانی موفق در طول آن ساعت نیاز دارد. این موضوع به الگوی ترافیک شما بستگی دارد، نه به مدل. محاسبات کامل، از جمله نحوه انتخاب بازه زمانی، در محاسبات نقطه سربهسر برای کش کردن پرامپت Claude آمده است.
دو جزئیات تعیین میکنند که آیا اصلاً از کش استفاده میکنید یا خیر. اول اینکه، پیشوندی که طول آن کمتر از حداقل طول تعیینشده توسط مدل باشد، بدون اطلاع قبلی کش نمیشود: تا اکتبر 2026، حداقل طول مستندشده برای Claude Opus 5، Opus 5.5 و Sonnet 5.5 برابر با 512 توکن، و برای Claude Sonnet 5 برابر با 1,024 توکن است؛ درخواستهای کوتاهتر به صورت عادی پردازش میشوند و هیچ خطایی برگردانده نمیشود. دوم اینکه، طول عمر کش از زمان شروع درخواستی که ورودی را مینویسد یا میخواند محاسبه میشود و هر بار خواندن، آن را بدون هزینه اضافی تمدید میکند. بنابراین، یک endpoint پرکار، کش 5 دقیقهای را برای همیشه زنده نگه میدارد. اما endpointای که هر ده دقیقه یکبار فراخوانی میشود، هر بار هزینه اضافی نوشتن را میپردازد و هرگز از مزایای آن بهرهمند نمیشود.
به جای فرض کردن، پاسخ را بررسی کنید. شیء usage مقادیر cache_creation_input_tokens و cache_read_input_tokens را گزارش میدهد. اگر تعداد خواندن (read count) در هر فراخوانی صفر باشد، به این معنی است که شما فقط هزینه نوشتن را میپردازید و هیچ بازگشتی دریافت نمیکنید.
جایی که دو حافظه پنهان به هم میرسند
یک system prompt طولانی نقطهای است که این دو با هم تلاقی میکنند و هزینه آن را از هر دو طرف بهطور همزمان پرداخت میکنید.
بهصورت محلی، یک system prompt با 20,000 توکن، حدود 2.4 GiB از حافظه KV cache را روی سرور Llama 3.1 8B با فرمت f16 اشغال میکند و این کار برای هر درخواست همزمانی که شامل آن باشد، بهصورت جداگانه انجام میشود. از راه دور، همان پیشوند (prefix) هزینه یک بار نوشتن در حافظه پنهان و سپس 0.1 برابر ورودی در هر فراخوانی بعدی را دارد، یا در مدلهای جدیدتر حتی کمتر است. هزینه محلی با تعداد کاربران شما مقیاسپذیر است. هزینه از راه دور با ترافیک شما مقیاسپذیر است و در زمانهای بیکاری (idle) بازنشانی میشود.
یک قابلیت محلی وجود دارد که شبیه به prompt caching ارائهدهندگان است و دائماً با آن اشتباه گرفته میشود: prefix caching. مستندات vLLM قابلیت automatic prefix caching را اینگونه توصیف میکند: «ذخیرهسازی KV cache پرسوجوهای موجود، بهطوری که یک پرسوجوی جدید بتواند در صورت اشتراک پیشوند با یکی از پرسوجوهای موجود، مستقیماً از آن KV cache استفاده مجدد کند». نسخه فعلی vLLM این قابلیت را بهصورت پیشفرض فعال دارد. سرور llama.cpp بهصورت پیشفرض برای هر slot یک prompt cache نگه میدارد و --cache-reuse N کوچکترین قطعهای را که برای استفاده مجدد تلاش میکند، تعیین مینماید.
آنچه prefix caching صرفهجویی میکند، توان محاسباتی prefill است. system prompt با 20,000 توکن شما بهجای پردازش در هر درخواست، تنها یک بار پردازش میشود که زمان رسیدن به اولین توکن (time to first token) را بهشدت کاهش میدهد. در vLLM، بلوکهای مشترک بهجای کپی شدن، مجدداً استفاده میشوند، بنابراین وضعیت حافظه نیز بهبود مییابد. آنچه این قابلیت هرگز انجام نمیدهد، کوچک کردن حافظه پنهانی است که باید برای توکنهای فعال فعلی نگه دارید. نگه داشتن وزنها (weights) در حافظه بین درخواستها، یک اهرم مرتبط اما جداگانه است که در نگه داشتن مدل Ollama در حافظه بین درخواستها به آن پرداخته شده است.
چه مواردی را روی سرور شخصی خود اندازهگیری کنیم
مدل را در context هدف خود بارگذاری کنید، سپس بهجای اعتماد به تخمینها، اعداد واقعی را بخوانید.
ollama ps
nvidia-smi --query-gpu=memory.used,memory.total --format=csv
free -gollama ps مدل بارگذاریشده را به همراه اندازه، context مورد استفاده و وضعیت اجرا روی GPU یا CPU فهرست میکند. ابتدا ستون context را بررسی کنید، زیرا مقدار پیشفرض اکنون به VRAM شما بستگی دارد. اگر مدلی که انتظار داشتید کاملاً روی GPU قرار بگیرد، وضعیت CPU split را گزارش میکند، به این معنی است که KV cache بخشی از آن را به بیرون رانده و سرعت تولید متن بهتبع آن کاهش مییابد. nvidia-smi مقدار دقیق VRAM را نشان میدهد و free -g همین کار را برای VPSهای مبتنی بر CPU انجام میدهد. context را مرحلهبهمرحله افزایش دهید، مدل را دوباره بارگذاری کنید و تغییر اعداد را مشاهده کنید. محاسبات شما و عدد گزارششده باید به هم نزدیک باشند. اگر چنین نیست، این اختلاف معمولاً ناشی از بافرهای محاسباتی خودِ runtime است و نه خطایی در فرمول.
اگر این اعداد شما را به سمت سختافزاری سوق میدهند که ترجیح میدهید اجاره نکنید، مقایسه با پرداخت به ازای هر توکن در مقایسه GPU VPS با توکنهای API بررسی شده است.
FAQ
آیا KV cache همان prompt caching است؟
خیر. KV cache حافظهای مختص به هر درخواست در داخل پردازش سرویسدهنده است که بردارهای کلید (key) و مقدار (value) را برای هر توکن در context فعلی نگه میدارد. این حافظه در RAM یا VRAM شما قرار دارد و با پایان درخواست آزاد میشود. قابلیت prompt caching در سرویسدهندهها، یک ویژگی مربوط به صورتحساب است که پیشوند (prefix) ثابتِ prompt را روی زیرساخت سرویسدهنده ذخیره میکند و در صورت ارسال مجدد آن، هزینه کمتری دریافت میکند. تمام شدن فضای KV cache باعث توقف بارگذاری مدل میشود، اما استفاده نکردن از prompt cache تنها باعث افزایش هزینه و زمان رسیدن به اولین توکن (time to first token) میشود.
چرا مدل من با context 2k بارگذاری میشود اما در 32k شکست میخورد؟
زیرا runtime کل فضای KV cache را در زمان بارگذاری تخصیص میدهد و اندازه آن را بر اساس context length پیکربندیشده تعیین میکند، نه بر اساس prompt ارسالی شما. برای مدل Llama 3.1 8B با دقت f16، حافظه کش به ازای هر توکن 128 KiB است؛ بنابراین 2k از context به میزان 0.25 GiB و 32k به میزان 4 GiB فضا نیاز دارد. وزنهای مدل در هر دو حالت جا میشوند، اما رزرو حافظه است که با شکست مواجه میشود. vLLM این وضعیت را با یک ValueError گزارش میدهد که در آن میزان حافظه مورد نیاز KV cache در مقایسه با حافظه موجود، به همراه حداکثر طول تخمینی مدل ذکر شده است و پیشنهاد میکند gpu_memory_utilization را افزایش یا max_model_len را کاهش دهید. در سیستمهای فقط CPU که Ollama یا llama.cpp را اجرا میکنند، ممکن است kernel out-of-memory killer پردازش را متوقف کند که میتوانید این موضوع را با dmesg -T | grep -i "killed process" تأیید کنید.
چگونه اندازه KV cache را برای مدل خود محاسبه کنم؟
عدد 2 را در تعداد لایهها، تعداد سرهای کلید/مقدار (key/value heads)، ابعاد سر (head dimension) و تعداد بایت به ازای هر عنصر ضرب کنید. این محاسبه، بایت به ازای هر توکن را به دست میدهد. سپس آن را در context length و تعداد درخواستهای همزمان ضرب کنید. تعداد لایهها و سرها را از config.json مدل بخوانید. برای f16 یا bf16 از 2 بایت به ازای هر عنصر استفاده کنید. یک کش q8_0 تقریباً نصف این مقدار و q4_0 تقریباً یکچهارم آن است.
آیا prompt caching حافظه مورد نیاز سرور شخصی من را کاهش میدهد؟
قابلیت prompt caching سرویسدهنده هیچ تأثیری بر سختافزار شما ندارد، زیرا فضای ذخیرهسازی در سمت سرویسدهنده است. معادل محلی آن، prefix caching است که توسط vLLM و سرور llama.cpp ارائه میشود. این قابلیت بردارهای کلید و مقدارِ از پیش محاسبهشده برای یک پیشوند مشترک را مجدداً استفاده میکند که باعث صرفهجویی در محاسبات prefill و کاهش زمان رسیدن به اولین توکن میشود. در vLLM، بلوکهای مشترک به جای کپی شدن، مجدداً استفاده میشوند، بنابراین مصرف حافظه نیز بهبود مییابد. هیچکدام از این ویژگیها، کش مورد نیاز برای توکنهای در حال پردازش را کوچک نمیکنند، بنابراین محاسبات context و همزمانی شما همچنان تعیینکننده حداقل حافظه مورد نیاز است.
آیا کش کردن prompt که فقط یک بار ارسال میکنم ارزش دارد؟
خیر. نوشتن در کش هزینه بیشتری نسبت به ورودی عادی دارد؛ تا اکتبر 2026، این هزینه برای گزینه 5 دقیقهای 1.25 برابر نرخ پایه است، بنابراین پیشوندی که هرگز در آن بازه زمانی دوباره ارسال نمیکنید، عملاً ضرر است. کش کردن زمانی سودآور است که پیشوند تکرار شود، مانند یک system prompt طولانی یا سندی که قرار است چندین سوال درباره آن بپرسید. برای اطمینان از اینکه به جای پرداخت هزینه برای نوشتن، در حال دریافت hit هستید، cache_read_input_tokens را در پاسخ API بررسی کنید.