Перейти к содержанию
Публикация AiManual

Почему локальная LLM на Windows теряет до половины скорости в неактивном окне - и как это исправить

Разбираем, почему локальная LLM на Windows может замедляться с 130-200 до 50-60 токенов/с после потери фокуса окна. Показываем, как проверить рост wait, отделит

Коротко

Что будет в материале

  1. 01

    Короткий ответ: почему локальная LLM замедляется в неактивном окне Windows

  2. 02

    Почему LLM теряет скорость после сворачивания окна: роль CPU и серверного цикла

  3. 03

    Как отличить задержки CPU от проблем GPU, питания и PCIe

  4. 04

    Как исправить проблему через Docker: detached и headless-запуск

Короткий ответ: почему локальная LLM замедляется в неактивном окне Windows

Если локальная LLM на Windows сразу ускоряется после возврата фокуса окну сервера, сначала проверяйте фоновой режим процесса и задержки CPU-серверного цикла. Такой признак не похож на постоянное ограничение RTX 5090, нехватку VRAM, перегрев или неисправность PCIe, хотя сам по себе ещё не доказывает конкретную причину.

В описанном сценарии скорость генерации падала примерно с 130-200 до 50-60 токенов в секунду, когда окно сервера становилось неактивным. Одновременно показатель wait увеличивался примерно с 16-17 до 38-41 мс. После переключения окна производительность возвращалась почти сразу.

Рабочая гипотеза выглядит так: GPU сохраняет способность быстро считать, но CPU-поток inference-сервера начинает с большей задержкой подавать ему очередную работу. Средняя загрузка видеокарты при этом может выглядеть нормально, а итоговый tokens/s всё равно заметно снижается. Для устранения симптома стоит проверить detached/headless-запуск через Docker или аналогичный фоновый режим нативной сборки Windows.

Главный признак - скорость возвращается сразу после переключения окна

Временная связь с фокусом окна важнее самого факта падения скорости. Если модель, запрос, контекст и параметры инференса не менялись, а скорость возвращается сразу после клика по окну, состояние интерфейса или процесса становится отдельной переменной диагностики.

Постоянный power limit, перегрев GPU или нехватка видеопамяти обычно не исчезают в момент переключения окна. После снятия нагрузки температура и частоты могут меняться постепенно, но мгновенное восстановление чаще указывает на изменение задержек выполнения, приоритета или поведения фонового процесса.

Это сильный сигнал, но не готовый диагноз. Фокус мог совпасть с завершением фоновой задачи, изменением запроса, освобождением памяти или другой случайностью. Поэтому замер повторяют несколько раз и записывают одновременно скорость, wait, загрузку CPU и GPU.

Почему это не обязательно проблема самой модели или квантования NVFP4

Модель около 27B в формате NVFP4 нужно рассматривать вместе с сервером, backend, драйвером и параметрами запуска. Формат весов влияет на объём памяти и характер вычислений, но он не объясняет сам по себе, почему одна и та же генерация ускоряется после возврата фокуса окну.

Для честного сравнения фиксируйте модель, версию весов, размер контекста, длину промпта и ответа, batch size, microbatch, число параллельных запросов, GPU offload и sampling. Любое изменение этих параметров может дать эффект, который ошибочно припишут Windows.

Практические различия между NVFP4, KV cache и backend подробно разобраны в материале о запуске Qwen 3.8 27B на одной RTX 5090. Для текущей проблемы ключевой вопрос другой: меняется ли работа серверного цикла при потере фокуса.

Что произошло в описанном сценарии: от 130-200 до 50-60 токенов в секунду

Последовательность наблюдений выглядит следующим образом:

  1. Сервер локальной LLM работает в активном окне Windows.
  2. Модель около 27B в формате NVFP4 генерирует ответ со скоростью примерно 130-200 токенов в секунду.
  3. Окно теряет фокус или сворачивается.
  4. Скорость снижается примерно до 50-60 токенов в секунду.
  5. Показатель wait растёт с 16-17 до 38-41 мс.
  6. Возврат фокуса почти сразу возвращает прежнюю скорость.

Такой сценарий превращает ощущение «LLM тормозит» в проверяемый тест. Но для публикации полноценного кейса нужны исходные логи, точная версия сервера, параметры запуска, версия драйвера, описание запроса и методика подсчёта токенов.

Условия, которые нужно зафиксировать до сравнения

  • Название и версия модели, формат весов и способ загрузки.
  • Размер контекста, фактическую длину промпта и максимальную длину ответа.
  • Batch size, microbatch size, число параллельных запросов и параметры sampling.
  • Inference runtime, backend, версию сервера и способ обращения к API.
  • Число слоёв на GPU, режим GPU offload, flash attention и другие backend-опции, если сервер их поддерживает.
  • Версию Windows, драйвера NVIDIA и состояние плана электропитания.
  • Способ запуска: видимое окно, свёрнутое окно, консольный процесс или контейнер Docker.

Меняйте одну переменную за раз. Если одновременно обновить драйвер, заменить параметры batch и перенести сервер в контейнер, источник улучшения останется неизвестным.

Что именно меняется при потере фокуса

При таком сравнении модель и GPU остаются прежними. Меняется состояние окна и, возможно, режим выполнения процесса. На практике нужно проверить, растёт ли задержка между итерациями декодирования, меняется ли CPU time серверного потока и сохраняется ли прежняя загрузка GPU.

Записывайте среднюю скорость отдельно для prompt processing и decode. Для короткого ответа статистика может быть нестабильной, поэтому полезно повторить один и тот же запрос несколько раз после одинакового прогрева модели.

Почему LLM теряет скорость после сворачивания окна: роль CPU и серверного цикла

Генерация токенов состоит из повторяющихся шагов. Сервер принимает запрос, готовит входные данные, запускает очередной проход модели, синхронизирует CPU и GPU, получает результат и передаёт следующий токен клиенту. На каждом шаге есть операции, которые зависят от CPU и состояния потока.

GPU может иметь высокие частоты и достаточный power budget, но простаивать между запусками ядер. Если CPU-поток задерживает следующую итерацию, пропускная способность всей цепочки снижается. Пользователь видит это как падение tokens/s, хотя проблема находится в промежутке между вычислениями.

Как задержка на CPU становится падением tokens/s

Упрощённый таймлайн одного шага выглядит так:

  1. CPU получает результат предыдущего шага.
  2. Сервер обрабатывает состояние запроса и готовит следующий вызов.
  3. Данные и команды передаются в GPU.
  4. GPU выполняет вычисления.
  5. CPU ждёт завершения нужной операции и отдаёт новый токен клиенту.

Если CPU-зависимый этап занимает больше времени, следующий запуск начинается позже. Например, рост ожидания на каждом шаге с 16 до 40 мс способен сильно уменьшить среднюю скорость, даже если вычислительная часть на GPU не изменилась.

Этот пример объясняет механизм, но не доказывает, что задержку вызвал именно планировщик Windows. Причиной могут быть блокировка потока, синхронизация, конкурирующий процесс, особенности API или конкретная реализация inference runtime.

Что может означать рост wait с 16-17 до 38-41 мс

wait показывает, что в измеряемой цепочке обработки появилось больше ожидания. Точная семантика зависит от сервера: метрика может учитывать ожидание CPU-потока, синхронизацию с GPU, очередь задач или другой этап.

Рост с 16-17 до 38-41 мс полезен как корреляция с падением скорости. Он показывает, что вместе со снижением tokens/s изменилось время ожидания. Но по одному этому числу нельзя определить, какой поток заблокирован и кто удерживает очередь.

Сначала найдите описание расчёта wait в документации конкретного runtime. Затем сопоставьте значение с временными отметками логов, CPU utilization отдельных ядер, GPU utilization и временем до первого токена. Если сервер не раскрывает формулу метрики, в статье или отчёте корректно указывать только факт изменения показателя.

Почему активное окно может маскировать проблему

Активное окно может менять условия выполнения процесса, его потоков и связанных с ним операций. На результат способны повлиять приоритет, фоновые ограничения, оконная оболочка, консольный хост и конкретная сборка сервера.

Когда окно находится в фокусе, серверный поток может получать процессорное время с меньшими задержками. После сворачивания или переключения на другое приложение задержки иногда увеличиваются. Это проверяемая гипотеза, а не универсальное правило Windows.

Headless-запуск убирает из эксперимента видимый GUI-элемент. Если производительность остаётся стабильной без окна, связь с оконным режимом становится убедительнее. Если результат не меняется, ищите причину в runtime, драйвере, нагрузке системы или параметрах модели.

Как отличить задержки CPU от проблем GPU, питания и PCIe

Диагностика должна сравнивать активный и неактивный режимы по нескольким группам показателей. Одна цифра, включая tokens/s или wait, не описывает весь путь запроса.

GPU: загрузка, частоты, температура и потребление

Для RTX 5090 или другой видеокарты запишите в обоих режимах:

  • GPU utilization;
  • частоту ядра и памяти;
  • температуру и горячую точку, если датчик доступен;
  • power draw и установленный power limit;
  • объём занятой VRAM и наличие признаков выгрузки в системную память.

Если при падении скорости частоты, потребление и температура почти не меняются, а загрузка GPU снижается или становится прерывистой, это поддерживает гипотезу о задержке до GPU-операции или между ними. Такой результат не исключает проблемы драйвера и не доказывает конкретный сбой CPU.

Если частота GPU заметно падает вместе с ростом температуры или power draw упирается в power limit, причина может быть аппаратной. В этом случае потеря фокуса окна могла совпасть с изменением нагрузки, но не обязана быть её источником.

PCIe и передача данных

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

Падение скорости только в неактивном окне само по себе не указывает на PCIe. Шина не должна менять режим из-за обычного переключения фокуса так, чтобы стабильно объяснить рост wait. Нужны логи, показатели передачи и повторяемый тест.

CPU и процесс сервера

Смотрите загрузку отдельных ядер, время CPU, задержки основного серверного потока, приоритет процесса и конкурирующие задачи. Средняя загрузка процессора может оставаться на уровне 20-30%, пока одно ядро или один поток ограничивает весь inference-цикл.

Сравните состояние процесса в трёх режимах: активное окно, неактивное или свёрнутое окно, headless-запуск. Если рост wait появляется только в одном из них и совпадает с изменением CPU time или задержек потока, гипотеза о серверном цикле получает дополнительное подтверждение.

Параметры инференса и воспроизводимость

Decode-производительность зависит от содержания запроса, длины контекста, длины ответа и конфигурации сервера. Поэтому одинаковое значение модели не гарантирует одинаковый результат на двух разных промптах.

Используйте фиксированный тестовый запрос, заранее прогрейте модель и сделайте несколько прогонов. Отдельно записывайте:

ПоказательЗачем нужен
Время до первого токенаПомогает отделить начальную обработку запроса от дальнейшей генерации.
Prompt processingПоказывает скорость обработки входного контекста.
Decode tokens/sПоказывает скорость последовательной генерации ответа.
Wait и latencyПомогают найти рост ожидания внутри серверного цикла.
CPU и GPU utilizationПозволяют сопоставить задержку с фактической загрузкой оборудования.

При сравнении конфигураций локальных LLM полезно учитывать поведение mmap, загрузку весов в RAM и работу KV cache. Отдельные примеры влияния этих настроек на скорость разобраны в материале о Qwen3.8-Flash-Next и llama.cpp.

Как исправить проблему через Docker: detached и headless-запуск

Docker позволяет запустить LLM-сервер как фоновый контейнер без постоянного интерактивного окна терминала. Это удобный способ проверить, связана ли потеря скорости с оконным режимом процесса.

Что даёт detached-режим

Флаг detached запускает контейнер в фоне и возвращает управление терминалу. Сервер продолжает слушать локальный порт или API, а его состояние проверяется командами Docker.

Общий шаблон выглядит так:

docker run -d --name llm-server [параметры доступа к GPU] [том модели] [том кеша] [порт] [образ inference-сервера] [параметры модели]

Это шаблон, а не универсальная команда. Название образа, формат параметров, способ передачи GPU, путь к модели и сетевые настройки зависят от конкретного inference runtime.

После запуска проверьте состояние контейнера:

docker ps --filter name=llm-server
docker logs --tail 100 llm-server

Detached-режим не гарантирует рост производительности. Если ограничение связано с драйвером, runtime, CPU или конфигурацией контейнера, перенос процесса в фон лишь изменит способ запуска.

Что проверить в Docker-конфигурации

  • Видит ли контейнер GPU и нужный CUDA или другой аппаратный backend.
  • Проброшены ли каталоги с моделью и кешами без лишних копирований.
  • Открыт ли нужный локальный порт и доступен ли API клиенту.
  • Не установлены ли лимиты CPU, памяти или shared memory, которые меняют поведение сервера.
  • Сохраняются ли права на файлы модели и логи после перезапуска.
  • Совпадают ли context size, batch size, parallelism, GPU offload и backend-опции с нативным запуском.

Особое внимание уделите доступу к GPU. Контейнер может успешно стартовать и отвечать по API, но использовать неподходящий backend или загружать часть вычислений на CPU. В таком случае сравнение с нативным запуском будет некорректным.

Как валидировать результат после исправления

Проведите тот же тест в нативном и контейнерном режиме. Используйте одну модель, один запрос, одинаковый контекст и одинаковые параметры генерации.

  • Запустите генерацию при активном окне.
  • Повторите её после переключения на другое приложение.
  • Сравните несколько прогонов detached-контейнера.
  • Запишите tokens/s, wait, время до первого токена, CPU utilization и GPU utilization.

Исправление можно считать подтверждённым, если detached-запуск сохраняет скорость в фоне на нескольких одинаковых прогонах, а рост wait исчезает или заметно уменьшается. Одного удачного запуска недостаточно.

Нативный Windows-запуск: аналогичный headless-режим без активного окна

Docker нужен не всем. Если inference runtime имеет консольный сервер и API, его можно запустить как фоновый процесс средствами Windows, без постоянного GUI-окна.

Какие варианты нативного фонового запуска рассмотреть

  • Консольный запуск. Запускайте сервер из терминала с параметрами модели и порта, если движок поддерживает работу без графической оболочки.
  • Планировщик заданий Windows. Настройте запуск при входе пользователя или старте системы. Проверьте режим запуска без интерактивного окна, рабочую директорию и учётную запись.
  • Поддерживаемый механизм фоновых процессов конкретного сервера. Некоторые сборки предлагают собственный режим службы или фоновый процесс. Используйте его только при наличии такой функции в выбранном runtime.

Для любого варианта проверьте доступ к GPU, переменные окружения, путь к модели, права на каталоги, сетевой порт и сохранение логов. Headless означает отсутствие зависимости от видимого окна, но не отменяет требования драйвера и backend.

Цель такого запуска проста: сервер остаётся доступен по локальному API или порту, пока пользователь работает в других приложениях. Если конкретный движок привязан к GUI и не умеет работать как консольный процесс, переносить его в headless-режим без отдельной проверки нельзя.

Приоритет процесса и план электропитания: не универсальная таблетка

Изменение приоритета процесса можно использовать как диагностический эксперимент. Если после повышения приоритета wait уменьшается, это указывает на конкуренцию за CPU или особенности планирования. Такой результат нужно записать вместе с настройками, а затем проверить на чистой системе.

План электропитания тоже способен менять частоты и время реакции CPU. Сравните режимы при одинаковой температуре и нагрузке. Высокий план питания не исправит блокировку потока, ошибочную синхронизацию или неверную конфигурацию GPU backend.

Не оставляйте повышенный приоритет и агрессивный план питания без проверки стабильности. Они могут ухудшить отзывчивость других приложений и скрыть настоящую причину задержек.

Чек-лист диагностики: что проверить, если инференс снова стал медленным

Шаг 1. Зафиксировать воспроизводимый тест

Выберите один запрос и заранее определите параметры:

  • модель и формат весов;
  • context size и фактический размер контекста;
  • batch size, microbatch и число параллельных запросов;
  • sampling и максимальную длину ответа;
  • версию драйвера и inference runtime.

Измерьте время до первого токена, prompt processing, decode, средний tokens/s и wait. Сделайте несколько прогонов после прогрева модели.

Шаг 2. Сравнить активное, свёрнутое и detached/headless-состояние

Проведите тест минимум в трёх режимах:

  1. Окно сервера находится в фокусе.
  2. Окно неактивно или свёрнуто.
  3. Сервер работает без интерактивного окна, через Docker detached или нативный headless-запуск.

Между прогонами не меняйте модель и параметры. Записывайте состояние окна, время теста и фоновые задачи Windows, чтобы не принять случайное совпадение за закономерность.

Шаг 3. Сопоставить wait с CPU и GPU

Сведите показатели в одну таблицу для каждого прогона:

ГруппаПоказателиЧто может означать изменение
Серверwait, latency, tokens/sРост wait вместе с падением decode указывает на дополнительное ожидание в измеряемом цикле.
CPUЗагрузка ядер, CPU time, состояние потокаПерегруженное или ожидающее ядро может ограничивать генерацию при невысокой средней загрузке.
GPUUtilization, частоты, power draw, температураСтабильные частоты при прерывистой загрузке поддерживают гипотезу о задержке подачи работы.
ПамятьVRAM, RAM, перемещения данныхЗаполнение памяти и выгрузка могут добавить задержки, не связанные с фокусом окна.

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

Шаг 4. Проверить окружение и конфигурацию сервера

  • Обновите или откатите драйвер только после сохранения исходной конфигурации для сравнения.
  • Проверьте GPU offload и число слоёв на видеокарте.
  • Сравните context size, batch size, microbatch и parallelism.
  • Проверьте flash attention и аналогичные backend-опции, если они поддерживаются выбранным сервером.
  • Убедитесь, что модель не загружается частично в RAM из-за нехватки VRAM.
  • Исключите антивирус, индексацию, обновления и другие задачи, которые совпадают по времени с тестом.

Названия параметров и их допустимые значения зависят от движка. Конфигурация, подходящая для llama.cpp, не переносится автоматически в другой сервер.

Шаг 5. Повторить тест после изменения режима запуска

После перехода на Docker detached или нативный headless-запуск повторите исходный тест без других изменений. Сравните средний и минимальный tokens/s, диапазон wait, время до первого токена, загрузку GPU и стабильность нескольких прогонов.

Сохраните команду запуска и логи. Без них невозможно понять, действительно ли помог режим без окна или результат изменился из-за другого batch size, context size или backend.

Когда проблема не в неактивном окне Windows

Одинаковое падение скорости может иметь несколько причин. Переключение окна остаётся полезным диагностическим признаком, но проверять нужно весь путь от запроса до GPU.

Изменился запрос, контекст или режим декодирования

Два запроса к одной модели могут давать разную скорость. На результат влияют длина промпта, уже заполненный контекст, длина ответа, sampling, batch size и число параллельных запросов.

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

Есть конкурирующие процессы или ограничения системы

Фоновая задача Windows может совпасть по времени с потерей фокуса. Проверьте антивирус, обновления, индексацию, браузер, другие GPU-приложения, энергосбережение, температуру CPU и GPU.

Если detached/headless-режим не меняет скорость, временно исключите конкурирующие процессы и повторите тест. Отдельно проверьте, не упирается ли система в RAM, VRAM или пропускную способность накопителя.

Метрика wait интерпретируется неправильно

Показатель wait может означать разные этапы в разных inference runtime. Рост с 16-17 до 38-41 мс подтверждает изменение метрики, но не объясняет его автоматически.

Сверяйте определение показателя с логами сервера и временными отметками операций. Если точная семантика неизвестна, формулируйте вывод аккуратно: в неактивном режиме увеличилось измеряемое ожидание, а скорость снизилась. Причину нужно подтверждать профилированием.

Итог: короткий алгоритм исправления падения скорости в фоне

  1. Зафиксируйте одинаковую модель, запрос, контекст, batch size, parallelism и версию runtime.
  2. Сравните активное, неактивное и detached/headless-состояние по tokens/s, wait, latency, CPU и GPU.
  3. Проверьте загрузку GPU, частоты, температуру, power limit, VRAM, PCIe и отдельные CPU-потоки.
  4. Если рост wait совпадает с падением скорости только в оконном фоне, перенесите сервер в Docker detached или используйте поддерживаемый headless-запуск нативной сборки Windows.
  5. Повторите тест несколько раз и сохраните логи, чтобы подтвердить эффект.

Для описанного кейса связь между неактивным окном, ростом wait и падением скорости до 50-60 токенов в секунду делает задержки серверного цикла на CPU логичным направлением проверки. Универсальность решения зависит от конкретного LLM-сервера, драйвера, GPU backend и конфигурации Windows.

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