Оптимизация VRAM для Qwen 35B: KV cache против квантизации | Ollama 2026 | AiManual
AiManual Logo Ai / Manual.
20 Мар 2026 Гайд

KV cache vs. весовая квантизация: Как экономить VRAM для параллельных запросов в Qwen 35B

Практическое руководство по экономии памяти для параллельных запросов в Qwen 35B. Сравниваем KV cache quantization и Q4_K_M, разбираем настройку Ollama и Open W

Почему ваш Qwen 35B падает при 5 одновременных пользователях?

Вы настроили Open WebUI с Ollama, запустили Qwen 35B, все работает отлично. Первый пользователь получает ответ за секунду. Второй - тоже нормально. Третий - уже медленнее. А четвертый и пятый видят либо таймаут, либо ошибку "Out of Memory". Знакомо? Видеопамять закончилась.

Виновник - не модель сама по себе, а способ ее работы с несколькими запросами. Каждый новый запрос - это отдельная копия контекста в памяти. И речь не только о весах модели, которые вы уже сжали до Q4_K_M. Основной пожиратель ресурсов при параллельном инференсе - это KV cache.

Типичная ошибка: думать, что квантизация весов решит все проблемы с памятью. При 5 параллельных запросах с контекстом 4096 токенов KV cache съедает столько же VRAM, сколько сами веса модели в Q4.

Что съедает память: анатомия параллельного инференса

Представьте, что Qwen 35B в формате Q4_K_M занимает около 21 ГБ VRAM. Кажется, на карте с 24 ГБ еще есть место. Но это иллюзия.

При каждом новом запросе модель создает отдельный KV cache. Это механизм, который хранит вычисленные ключи и значения для всех предыдущих токенов, чтобы не пересчитывать их заново. Без него генерация была бы в 10 раз медленнее. Но за скорость приходится платить памятью.

Размер KV cache для одного запроса рассчитывается так:

# Формула упрощенно
kv_cache_size = 2 * batch_size * num_layers * num_heads * head_dim * seq_len * dtype_size

Для Qwen 35B (35 миллиардов параметров, 40 слоев, 40 голов внимания) при контексте 4096 токенов в формате float16:

  • 1 запрос: ~3.5 ГБ VRAM
  • 5 запросов: ~17.5 ГБ VRAM
  • Плюс веса модели: ~21 ГБ
  • Итого: ~38.5 ГБ - уже не влезает в 24 ГБ

И это без учета памяти для самого инференса, промежуточных активаций и других служебных данных. Реальная потребность еще выше.

💡
Вспомните статью "Конец эпохи квадратичной сложности" - там подробно разбирается, как KV cache стал спасательным кругом и новой проблемой одновременно.

Две стратегии выживания: ужать веса или ужать кэш

У вас есть два основных рычага, чтобы впихнуть больше запросов в ограниченную VRAM:

1 Весовая квантизация: жертвовать точностью параметров

Это то, что все делают по умолчанию: скачивают Qwen 35B не в FP16, а в Q4_K_M или даже Q3_K_M. Каждый параметр модели хранится не в 16 битах, а в 4 или 3 битах с некоторыми компенсациями.

На 20.03.2026 актуальные форматы для llama.cpp (который использует Ollama) - это Q4_K_M, Q5_K_M, и относительно новый Q3_K_M. Q2_K уже считается экстремальным и для 35B моделей дает катастрофическое падение качества.

Формат Размер Qwen 35B Экономия против FP16 Потери на MMLU Pro (2026)
FP16 ~70 ГБ 0% 81.2% (база)
Q8_0 ~35 ГБ 50% 80.9% (-0.3)
Q6_K ~26.5 ГБ 62% 80.5% (-0.7)
Q4_K_M ~21 ГБ 70% 79.1% (-2.1)
Q3_K_M ~17.5 ГБ 75% 74.3% (-6.9)

Обратите внимание на провал между Q4_K_M и Q3_K_M - почти 5 пунктов на MMLU Pro. Это та самая граница, за которой модель перестает быть полезной для сложных задач. Как мы узнали из статьи про Qwen3.5-122B, большие модели особенно чувствительны к агрессивной квантизации.

2 Квантизация KV cache: жертвовать точностью контекста

Вот это менее известный, но более эффективный для параллельных запросов прием. Вместо сжатия весов модели, вы сжимаете KV cache для каждого запроса.

На 20.03.2026 llama.cpp (через Ollama) поддерживает несколько форматов для KV cache:

  • f16 - полная точность, много памяти
  • q8_0 - 8-битное квантование, экономия 50%
  • q4_0 - 4-битное квантование, экономия 75%
  • q4_1 - улучшенное 4-битное с масштабом

Но вот загвоздка: Qwen 35B архитектурно чувствителен к точности KV cache. Как мы обсуждали в статье про bf16 KV cache, использование неподходящего формата может тихо убить качество ответов на 20-30%.

Для Qwen 35B в 2026 году я рекомендую использовать q8_0 для KV cache как оптимальный баланс. q4_0 уже дает заметные артефакты на длинных контекстах, особенно в логических рассуждениях.

KV cache формат Размер на запрос (4096 токенов) Экономия против f16 Влияние на качество
f16 ~3.5 ГБ 0% Нет
q8_0 ~1.75 ГБ 50% Минимальное (0.5-1.5%)
q4_0 ~0.875 ГБ 75% Заметное (3-8%, зависит от задачи)

Считаем вместе: какая стратегия выгоднее?

Допустим, у вас RTX 4090 с 24 ГБ VRAM. Вы хотите запустить Qwen 35B с поддержкой минимум 4 параллельных запросов.

Сценарий 1: Только весовая квантизация

  • Модель: Qwen 35B Q4_K_M = ~21 ГБ
  • KV cache для 4 запросов в f16: 4 × 3.5 ГБ = 14 ГБ
  • Итого: 35 ГБ → не влезает

Сценарий 2: Комбинированный подход

  • Модель: Qwen 35B Q4_K_M = ~21 ГБ
  • KV cache для 4 запросов в q8_0: 4 × 1.75 ГБ = 7 ГБ
  • Итого: 28 ГБ → все еще много

Сценарий 3: Агрессивная оптимизация

  • Модель: Qwen 35B Q4_K_M = ~21 ГБ
  • KV cache для 4 запросов в q4_0: 4 × 0.875 ГБ = 3.5 ГБ
  • Итого: 24.5 ГБ → почти влезает, но качество страдает

Сценарий 4: Умеренная весовая квантизация + KV cache оптимизация

  • Модель: Qwen 35B Q6_K = ~26.5 ГБ
  • KV cache для 4 запросов в q8_0: 4 × 1.75 ГБ = 7 ГБ
  • Итого: 33.5 ГБ → не работает

Видите проблему? Даже с оптимизациями 4 параллельных запроса в 24 ГБ не влезают. Нужны дополнительные трюки.

Пошаговый план: как настроить Ollama и Open WebUI для максимума параллелизма

1 Скачиваем правильную версию модели

На 20.03.2026 в Ollama уже есть официальная поддержка Qwen 35B с разными квантизациями. Но если вы хотите полный контроль, используйте llama.cpp напрямую.

# Скачиваем Qwen 35B в Q4_K_M (баланс качества и размера)
ollama pull qwen2.5:35b-q4_K_M

# Или создаем свой Modelfile с явными параметрами
cat > Modelfile << EOF
FROM ~/.ollama/models/blobs/sha256-...
PARAMETER num_ctx 4096
PARAMETER num_batch 512
PARAMETER num_gpu_layers 99
PARAMETER main_gpu 0
TEMPLATE "{{ .Prompt }}"
PARAMETER stop "<|im_end|>"
PARAMETER stop "<|endoftext|>"
EOF

ollama create qwen35b-custom -f Modelfile

Не используйте устаревшие имена моделей вроде "qwen:35b". На 2026 год актуально "qwen2.5:35b" или "qwen3:35b", в зависимости от последнего релиза Alibaba. Проверьте библиотеку Ollama для точных названий.

2 Настраиваем квантизацию KV cache в Ollama

Ollama 0.5.x (актуальная на 2026 год) поддерживает настройку формата KV cache через переменные окружения или конфигурационные файлы.

# Способ 1: Переменная окружения для всей системы
export OLLAMA_KV_CACHE_TYPE=q8_0

# Способ 2: В сервисном файле systemd (если Ollama работает как сервис)
sudo systemctl edit ollama.service

# Добавляем в секцию [Service]:
Environment="OLLAMA_KV_CACHE_TYPE=q8_0"
Environment="OLLAMA_NUM_PARALLEL=4"

# Способ 3: Для конкретного запуска
OLLAMA_KV_CACHE_TYPE=q4_0 OLLAMA_NUM_PARALLEL=3 ollama serve

OLLAMA_NUM_PARALLEL контролирует, сколько запросов модель может обрабатывать одновременно. Но это не волшебная палочка - если установить значение 8 на карте с 24 ГБ, вы получите OOM при первой же попытке обработки 8 запросов.

3 Настраиваем Open WebUI для работы с ограниченной памятью

Open WebUI по умолчанию пытается отправлять запросы параллельно, что может убить вашу видеокарту. Нужно это ограничить.

# docker-compose.yml для Open WebUI
version: '3.8'

services:
  open-webui:
    image: ghcr.io/open-webui/open-webui:main
    container_name: open-webui
    ports:
      - "3000:8080"
    volumes:
      - open-webui-data:/app/backend/data
    environment:
      - OLLAMA_BASE_URL=http://host.docker.internal:11434  # Для Mac/Windows
      - WEBUI_SECRET_KEY=your_secret_key_here
      - MAX_CONCURRENT_REQUESTS=3  # Критически важный параметр!
      - REQUEST_TIMEOUT=600
    restart: unless-stopped

volumes:
  open-webui-data:

MAX_CONCURRENT_REQUESTS=3 означает, что Open WebUI не будет отправлять более 3 запросов к Ollama одновременно. Это защита от перегрузки.

4 Включаем Flash Attention и другие оптимизации

На 20.03.2026 Flash Attention 3 (или даже 4) должен быть стандартом. В llama.cpp он включается автоматически для поддерживаемых архитектур. Но проверьте:

# Проверяем, собран ли llama.cpp с поддержкой Flash Attention
ollama --version
# Должна быть строка с поддержкой CUDA и flash_attn

# Если нет, пересобираем Ollama из исходников с флагами:
CMAKE_ARGS="-DLLAMA_CUDA=on -DLLAMA_CUDA_FORCE_MMQ=on -DLLAMA_FLASH_ATTN=on" \
BUILD_TYPE=Release \
make -j$(nproc)

Flash Attention не экономит память напрямую для KV cache, но ускоряет обработку, позволяя быстрее освобождать ресурсы для новых запросов.

Сколько пользователей выдержит ваше железо: реалистичные расчеты

Давайте забудем про маркетинговые цифры и посчитаем реальную пропускную способность для разных конфигураций.

Конфигурация VRAM Модель + KV cache Макс. параллельных запросов Рекомендовано пользователей
RTX 4090 24 ГБ Q4_K_M + q8_0 cache 2-3 5-8 (с очередями)
RTX 6000 Ada 48 ГБ Q6_K + q8_0 cache 6-8 15-20
2x RTX 4090 48 ГБ Q4_K_M + f16 cache 8-10 20-25
A100 80GB 80 ГБ Q8_0 + f16 cache 12-15 30-40

"Рекомендовано пользователей" предполагает, что не все пользователи активны одновременно. Open WebUI ставит запросы в очередь, если достигнут лимит MAX_CONCURRENT_REQUESTS.

💡
Если вам нужно обслуживать действительно много пользователей, изучите статью про оптимизацию vLLM. vLLM использует более эффективный memory pooling для KV cache, чем llama.cpp, что позволяет обслуживать больше пользователей на той же видеопамяти.

Ошибки, которые сломают вашу систему

Я видел эти ошибки десятки раз. Не повторяйте их.

Ошибка 1: Слепая экономия на квантизации KV cache

Что делают: Устанавливают OLLAMA_KV_CACHE_TYPE=q4_0 для максимальной экономии.

Что происходит: Первые 10-20 запросов работают нормально. Потом пользователи начинают жаловаться, что модель "тупеет" - путает факты, теряет контекст диалога, генерирует противоречивые ответы. Особенно заметно на задачах с длинными контекстами (код, документы).

Почему: Q4 для KV cache - это слишком агрессивно для Qwen 35B. Ошибки квантования накапливаются в длинных цепочках внимания. Модель буквально "забывает", что было в начале промпта.

Ошибка 2: Игнорирование OLLAMA_NUM_PARALLEL

Что делают: Настраивают Open WebUI на 10 параллельных запросов, но забывают про OLLAMA_NUM_PARALLEL.

Что происходит: Open WebUI отправляет 10 запросов одновременно. Ollama принимает их и пытается обработать. Видеопамять переполняется. Система зависает или падает. Пользователи видят 502 ошибку.

Почему: OLLAMA_NUM_PARALLEL должен быть равен или меньше MAX_CONCURRENT_REQUESTS в Open WebUI. А лучше - установить оба значения в соответствии с реальными возможностями железа.

Ошибка 3: Неадекватные ожидания от железа

Что делают: Прочитали, что "Qwen 35B работает на RTX 4090", и ожидают 20 параллельных пользователей.

Что происходит: Разочарование, гнев, негативные отзывы о модели (хотя проблема в железе).

Почему: "Работает" и "работает с N параллельными пользователями" - это разные вещи. Одна 35B модель на 24 ГБ карте - это одиночный пользователь или максимум 2-3 параллельных. Для большего нужно либо больше VRAM, либо меньшая модель, либо продвинутые техники распределения между картами.

Ответы на частые вопросы

В чем разница между q4_0 и q4_1 для KV cache?

q4_0 использует одно масштабирующее значение на весь тензор. q4_1 использует отдельные scale для положительных и отрицательных значений, что дает немного лучшую точность, но занимает чуть больше памяти (примерно на 0.1 бит на вес). Для KV cache в 2026 году q4_1 предпочтительнее, если ваш llama.cpp его поддерживает.

Можно ли динамически менять формат KV cache в зависимости от нагрузки?

Нет, внутри одного запуска Ollama - нельзя. Формат KV cache фиксируется при запуске сервера. Можно запустить несколько инстансов Ollama с разными настройками и балансировать между ними, но это сложно.

Почему при 5 параллельных запросах скорость генерации падает в 10 раз?

Потому что видеопамять переполнена. GPU начинает использовать системную память через swap, и latency взлетает. Это называется "memory thrashing". Решение - уменьшить количество параллельных запросов или использовать более агрессивную квантизацию.

Какая модель лучше для 10+ параллельных пользователей: Qwen 35B в Q4 или Qwen 14B в Q8?

С точки зрения качества на пользователя - Qwen 35B в Q4. С точки зрения количества пользователей - Qwen 14B в Q8. 14B в Q8 займет ~15 ГБ, оставив место для KV cache 8-10 пользователей. Но качество будет хуже. Тест на своих данных перед выбором.

Есть ли способ вообще не хранить KV cache?

Технически - да, можно генерировать каждый токен с полным пересчетом (это называется "no cache" или "recompute"). Но скорость упадет в 10-100 раз. Не делайте этого в продакшене. Разве что для отладки.

Что делать, если все равно не хватает памяти

Если вы перепробовали все оптимизации, но 24 ГБ все равно мало для ваших задач, есть три пути:

  1. Апгрейд железа. RTX 6000 Ada (48 ГБ) или H100 (80 ГБ) решат проблему, но стоят как хороший автомобиль.
  2. Использовать меньшую модель. Qwen 14B или даже 7B с хорошим квантованием могут давать приемлемое качество для многих задач, а параллелизм будет выше. Смотрите статью про выбор моделей для кодинга - там принципы применимы и к другим задачам.
  3. Перейти на vLLM с PagedAttention. vLLM умеет более эффективно управлять памятью для KV cache, позволяя втиснуть больше параллельных запросов. Но настройка сложнее, чем Ollama.

Мой прогноз на конец 2026 года: мы увидим появление встроенной интеллектуальной квантизации KV cache, которая будет динамически выбирать точность для разных частей контекста. Слои, отвечающие за ключевую информацию, будут храниться с высокой точностью, а менее важные - с низкой. Но пока это лишь исследования.

А пока - выбирайте q8_0 для KV cache, Q4_K_M для весов, и не ждите чудес от одной видеокарты. Параллельные запросы к большим моделям - это всегда компромисс между количеством пользователей, скоростью и качеством.

Подписаться на канал