Каждая карточка локальной модели теперь хвастается окном в 128k. На десктопе эта цифра — договор аренды, который ваша RAM подписывает не читая. Что решает, докуда вы дотянете длинный документ, — не веса. Это KV-кэш, и он масштабируется с контекстом как налог, согласованный в чужой валюте.
Окно контекста — не функция, которую включают. Это аренда памяти, которую вы платите каждую секунду работы сервера.
Сколько RAM на самом деле стоит контекст 128k?
Арифметика публичная и короткая. Как раскладывает Transformer Inference Arithmetic, KV-кэш хранит два тензора — ключи и значения — для каждого слоя, каждой KV-головы, каждого токена:
cache_bytes = 2 × слои × контекст × kv_головы × head_dim × байт_на_элемент Возьмём Llama 3.1 8B: 32 слоя, 8 KV-голов, head_dim 128 — прямо из config.json на Hugging Face. При 131 072 токенах в fp16 (2 байта):
2 × 32 × 131072 × 8 × 128 × 2 = 17 179 869 184 байта ≈ 16 GiB Веса той же модели в fp16 весят около 16 GiB. При максимальном контексте кэш так же велик, как модель, к которой он относится. «8B-модель, помещающаяся в 16 GiB» молча превращается в проблему на 32 GiB, как только вы поднимаете num_ctx до 128k.
Почему кэш растёт вместе с окном?
Потому что внимание обязано сравнить каждый токен со всеми предыдущими, а инференс авторегрессионен — ничего не пересчитывается, всё запоминается. В этом весь трюк, делающий генерацию быстрой: ключи и значения каждого токена вычисляются один раз и сохраняются. Память на токен постоянна, значит общий расход линеен по контексту. 128k — не «число побольше». Это 128× кэша окна в 1k, выделенного на всю сессию.
Grouped-query attention (GQA) — скидка на стороне модели: меньше KV-голов, стройнее кэш. Qwen2.5 7B использует 28 слоёв и всего 4 KV-головы (config здесь), поэтому то же окно 128k стоит около 7 GiB в fp16 — настоящее облегчение и одна из причин, почему эти модели дружелюбнее к скромным машинам.
| Модель (fp16) | Слои × KV-головы | KV-кэш @ 128k | Веса | Кэш к весам |
|---|---|---|---|---|
| Llama 3.1 8B | 32 × 8 | ~16 GiB | ~16 GiB | ~100 % |
| Qwen2.5 7B | 28 × 4 | ~7 GiB | ~15 GiB | ~47 % |
Посчитайте сами — вся проверка умещается вот в этом:
def kv_cache_gib(layers, kv_heads, head_dim, ctx, bytes_per=2):
return 2 * layers * ctx * kv_heads * head_dim * bytes_per / 2**30
print(kv_cache_gib(32, 8, 128, 131_072)) # Llama 3.1 8B -> 16.0
print(kv_cache_gib(28, 4, 128, 131_072)) # Qwen2.5 7B -> 7.0 О чём маркетинговая цифра умалчивает
Вот честная бухгалтерия этого аргумента — включая места, где он гнётся:
- Prefill — второй счёт. Прежде первого токена ответа весь prompt обрабатывается разом. Prompt на 100k токенов — это 100k токенов пережевать до первого символа. На 3060 это минуты, а не миллисекунды.
- Sliding-window модели ломают расчёт — в вашу пользу. Архитектуры типа Mistral ограничивают внимание фиксированным окном на слой, и кэш перестаёт расти. Простая формула их переоценивает. Этот текст — про трансформеры с полным вниманием, то есть большинство того, что люди запускают.
- Квантование KV реально, но частично. llama.cpp умеет держать KV в q8_0 — кэш примерно вдвое меньше при некотором риске для качества. Половина от 16 GiB — всё ещё 8 GiB.
- В спеках редко указывают KV-головы. Придётся копаться в config.json — что само по себе говорит, как эта цифра задумана для чтения.
Ничто из этого не делает длинный контекст бесполезным. RAG существует именно потому, что забивать окно — дорогой способ сказать «прочитай раздел 4». Но карточка, печатающая «128k» без счёта за память, продаёт машину по максимальной скорости и ни словом о бензобаке.
Никто не читает 128 000 токенов. Ваша RAM платит за них всё равно — за каждый.
Лекарство скучное: выставляйте контекст по тому, что ваша нагрузка реально использует. Окно 32k стоит четверть кэша 128k и покрывает почти любой prompt, который разработчик-одиночка действительно отправляет. Это та же дисциплина учёта, что в арифметике RAM для локальных LLM: размер модели — лишь половина бюджета, а уровни квантования ужимают веса, но расчёт кэша не трогают.
Если кэш — настоящая цена контекста, почему карточки продают окно и никогда — счёт? И раунд исповеди: какой самый большой контекст вы реально заполнили до последнего токена — и оправдал ли результат гигабайты? Комментарии открыты; следующая часть серии встанет на сторону проигравшего.
FAQ
Сколько RAM требует окно контекста 128k?
Для Llama 3.1 8B в fp16 один лишь KV-кэш весит около 16 GiB при 131 072 токенах — примерно столько же, сколько веса модели. Формула: 2 × слои × контекст × KV-головы × head_dim × байты.
Почему длинный контекст расходует больше памяти?
Каждый токен в кэше хранит тензоры ключей и значений для каждого слоя внимания. Удвоили контекст — удвоился кэш. Аллокация существует, даже если окно вы никогда не заполните.
Как уменьшить память KV-кэша в локальных LLM?
Выставите контекст по реальному использованию, берите модели с grouped-query attention (меньше KV-голов), квантуйте KV-кэш в q8 там, где поддерживает рантайм, или используйте sliding-window и гибридные архитектуры.
— mrsaynothing
— mrsaynothing
Мнения прогружены тестом до публикации. В основном.
Следующий аргумент — на почту
Одно письмо на пост. Согласиться или разобрать.
что это?git stash: один файл, не трогая остальные
Нравятся заметки? Я зарабатываю этим на жизнь. нанять меня