Назад до блогу

Вирішення проблемПосібник

Обробка запитів у llama.cpp у 35 разів повільніша? Виправте KV-кеш q5_1

Flash attention плюс KV-кеш q5_1 можуть перенести увагу на CPU. Як це підтвердити, виправити з q8_0 і коли API простіше.

Автор: CheapTokens TeamОпубліковано 5 хв читання

Термінал показує GPU на 0 відсотків і CPU на 930 відсотків під час обробки запиту в llama.cpp
На цій сторінці
  1. Як це виглядає на екрані
  2. Чому менший кеш робить це настільки повільнішим
  3. Перевірте це за дві хвилини
  4. Виправлення 1: використовуйте q8_0 (або перезберіть)
  5. Коли тюнінг перестає бути вартим
  6. Порядок дій

Ваша локальна модель відповідає нормально, але тепер кожен довгий запит висить півтори хвилини, поки GPU простоює. Якщо ви запускаєте llama.cpp з -fa on і KV-кешем q5_1, це, ймовірно, причина: стандартна збірка CUDA не має ядра flash-attention для цього типу кешу, тому крок уваги тихо виконується на CPU. Перемикання кешу на q8_0 підняло в одного користувача швидкість обробки запиту з 38 до 1348 токенів/с.

Наведені нижче цифри походять з issue #28455 у llama.cpp (відкрито 5 вересня 2026, збірка 10791, перевірено 28 жовтня 2026). Один користувач, одна RTX 4080, одна модель на 27B, тож сприймайте їх як розібраний приклад, а не як закон. Їхній аналіз першопричини теж їхній: супровідник назвав це «відомою проблемою» і закрив issue наступного дня.

Як це виглядає на екрані#

Налаштування були звичайні. Хтось хотів контекст на 131072 токени на одній карті з 16 ГБ, тож зменшив KV-кеш до q5_1:

bash
llama-server -m model.gguf -ngl 99 -fa on -c 131072 -np 4 \
  --cache-type-k q5_1 --cache-type-v q5_1

Потім він надіслав запит на 3.6K токенів. Обробка запиту йшла зі швидкістю 38.4 токена/с, а декодування — 6.5 токена/с. Поділіть: 3,600 ÷ 38.4 = 93.75, тобто близько 94 секунд до першого слова. З q8_0 той самий запит ішов зі швидкістю 1348 токенів/с: 3,600 ÷ 1,348 = 2.67, менше трьох секунд.

Підказка — у диспетчері завдань. Під час повільних запусків GPU був близько 0%, а CPU показував близько 930% (зайнято дев'ять-десять ядер). GPU, що простоює, поки ви «працюєте на GPU», — це симптом, який варто шукати.

Швидкість обробки запиту з KV-кешем q5_1 і q8_0
Prompt eval на запиті 3.6K токенів, RTX 4080, llama.cpp issue #28455 (5 вересня 2026)

Чому менший кеш робить це настільки повільнішим#

Flash attention — це об'єднане ядро GPU. Стандартна збірка CUDA постачає його лише для деяких пар типів KV-кешу. Для q4_0, q4_1, q5_0 і q5_1 ядра немає, тож llama.cpp не зупиняється з помилкою. Він переносить цю операцію на CPU і продовжує. Усе працює, просто приблизно в 35 разів повільніше на обробці запиту (1348 ÷ 38.4 = 35.1).

Саме це «тихо» і є всією проблемою. Ніщо у відповіді вам про це не каже. Обробка запиту падає найбільше (приблизно в 35×), і генерація теж сповільнюється: 6.5 токена/с проти 30.1 з q8_0 у тому самому issue.

Перевірте це за дві хвилини#

Запустіть llama-bench з тими самими типами кешу, які ви використовуєте в сервері, по одному разу для кожного варіанта, і стежте за GPU в іншому терміналі:

bash
llama-bench -m model.gguf -ngl 99 -fa 1 -ctk q8_0 -ctv q8_0 -p 512 -n 0
llama-bench -m model.gguf -ngl 99 -fa 1 -ctk q5_1 -ctv q5_1 -p 512 -n 0
nvidia-smi -l 1

Результати pp512 у користувача на Qwen 27B Q5_K_XL: 1511.92 токена/с для q8_0/q8_0, близько 197 для q5_1/q5_1 у стандартній збірці. Якщо ваші два рядки показують такий розрив і nvidia-smi показує, що GPU простоює під час другого, ви знайшли це.

Перш ніж щось виправляти, зіставте побачене з імовірною причиною:

З'єднайте пари

Зіставте кожен симптом із його причиною

  • GPU на ~0%, CPU на ~930% під час довгого запитуУвага виконується на CPU
  • Декодування падає до ~6.5 токена/с проти 30.1 з q8_0Генерація теж сповільнюється, не лише обробка запиту
  • Ті самі прапорці швидкі з q8_0Ця пара типів кешу не має ядра flash-attention для GPU у стандартній збірці

Виправлення 1: використовуйте q8_0 (або перезберіть)#

Найшвидше виправлення — змінити два прапорці й залишити все інше:

bash
llama-server -m model.gguf -ngl 99 -fa on -c 131072 -np 4 \
  --cache-type-k q8_0 --cache-type-v q8_0

В issue це дало 1348 токенів/с на обробці запиту і 30.1 токена/с на декодуванні (замість 6.5). Ціна — пам'ять: кеш q8_0 більший за q5_1, тож перевірте, чи ваша довжина контексту все ще вміщується у VRAM.

Якщо вам конче потрібен q5_1 через пам'ять, перезберіть з усіма комбінаціями кешу для flash-attention:

bash
cmake -B build -DGGML_CUDA=ON -DGGML_CUDA_FA_ALL_QUANTS=ON
cmake --build build --config Release -j

Ця збірка прогнала q5_1 зі швидкістю 1509.70 токена/с на pp512 і 1162 токена/с на запиті 3.6K токенів, з декодуванням на 38.5 токена/с. Коментатор в issue зауважив, що перенесення роботи між бекендами може трохи змінити числові результати, і запропонував порівняти perplexity між збірками. Якщо вам важливі точні вихідні дані, зробіть це.

Порівняйте

Яке виправлення підходить вашій карті?

Кеш q8_0

Змініть --cache-type-k і --cache-type-v на q8_0. Без перезбирання. Використовує більше VRAM, ніж q5_1, тож перевірте розмір контексту, який вміщується.

Перезбирання з FA_ALL_QUANTS

Залиште q5_1 і зберіть з -DGGML_CUDA_FA_ALL_QUANTS=ON. Той самий обсяг VRAM, що й раніше, але ви підтримуєте власну збірку, і компіляція триває довше.

Відповідь супровідника «відома проблема» не каже, чи змінили це поточні версії llama.cpp. Чесна перевірка — пара llama-bench вище: якщо обидва рядки близькі, у вас усе гаразд.

Коли тюнінг перестає бути вартим#

Припустімо, ви налагодили виправлення і тепер зважуєте наступний раунд: більше прапорців, друга GPU, менший квант. Якщо робота — це підсумки, рев'ю коду чи вилучення даних на запитах у кілька тисяч токенів, запитайте, скільки запит коштує вам у часі та зусиллях, а не лише у ватах.

Ось заявлене навантаження для порівняння. Скажімо, завдання підсумовування надсилає 3,600 вхідних токенів і отримує 500 назад, 1,000 разів. На CheapTokens це близько $0.104 на DeepSeek-V4-Flash (контекст 380K, до 32,768 вихідних токенів на відповідь, одна ціна в будь-яку годину) і близько $0.092 на GLM-5.3-Flash (контекст 390K). Таблиця рахує вхід і вихід разом, з колонкою OpenRouter для порівняння:

Модель За запит 1 000 запитів OpenRouter, 1 000 запитів
GLM-5.3-Flash $0.000092 $0.092 $0.187
DeepSeek-V4-Flash $0.0001 $0.104 $0.249
MiniMax-M2.7 $0.00026 $0.255 $1.68

3 600 вхідних і 500 вихідних токенів на запит. Ціни за 1 млн токенів актуальні та змінюються разом із нашими тарифами; стовпець OpenRouter показує довідкові ціни для того самого навантаження.

API не хвилює ваш тип кешу, ваша VRAM чи прапорці збірки; немає чого переносити на CPU. Спершу надішліть кілька своїх реальних запитів і оцініть відповіді самі. Крок невеликий: ендпоінт говорить форматом OpenAI Chat Completions, тож більшості локальних клієнтів потрібна лише нова базова URL-адреса.

bash
curl https://api.cheaptokens.dev/v1/chat/completions \
  -H "Authorization: Bearer $CHEAPTOKENS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "deepseek-ai/DeepSeek-V4-Flash-0731", "messages": [{"role": "user", "content": "Summarize the text below in 5 bullets: ..."}], "max_tokens": 500}'

Повне налаштування є в документації CheapTokens, а поточні ціни для кожної моделі — на сторінці цін. Для довшого огляду апаратного забезпечення проти API дивіться Запуск DeepSeek локально або через API. Якщо хочете побачити, як вхід і вихід тарифікуються окремо, вхідні vs вихідні токени це пояснює.

Порядок дій#

Розставте по порядку

Розставте кроки усунення несправностей по порядку

  1. Запустіть llama-bench для q8_0 і q5_1 і стежте за nvidia-smi
  2. Перемкніть обидва типи кешу на q8_0
  3. Перевірте, чи довжина контексту все ще вміщується у VRAM
  4. Перезберіть з GGML_CUDA_FA_ALL_QUANTS=ON, лише якщо вам потрібен q5_1
  5. Порівняйте той самий запит на API, якщо локальне налаштування все ще не варте зусиль

Ще одне застереження щодо джерел: увесь діагноз спирається на один публічний issue і одну GPU. Сприймайте 1348 і 38.4 як те, що сталося там, і виміряйте власну карту, перш ніж довіряти будь-чому з цього.