Частота опроса USB-устройств ввода давно перестала быть числом на коробке и превратилась в предмет споров, где маркетинг опережает инженерию. Производители мышей пишут "8000 Гц" крупным шрифтом, обзорщики машут графиками, а системный инженер смотрит на это иначе: каждый опрашенный отчёт это прерывание, пробуждение ядра, короткий путь через стек драйверов и возврат. Ниже разобрано, как устроен опрос по спецификации, чем 1000 Гц отличается от 8000 Гц на уровне хост-контроллера, почему оценки "пять процентов процессора" ничего не значат без контекста, где на самом деле кончается запас у корневых хабов и по каким правилам разводить высокорейтовую периферию по портам, чтобы не платить за результат, которого нет.

Interrupt transfers и bInterval как основа опроса

Мышь, клавиатура и геймпад общаются с хостом через interrupt transfers. Слово interrupt здесь историческое и вводит в заблуждение: устройство не может само прервать хост. Наоборот, хост-контроллер сам по расписанию спрашивает устройство, есть ли свежий отчёт. Период этого расписания задаётся дескриптором конечной точки, полем bInterval. Для low-speed и full-speed устройств bInterval задаётся в миллисекундах от 1 до 255, для high-speed в степенях двойки микрофреймов. Отсюда круглые числа, к которым все привыкли: 125 Гц это опрос раз в 8 мс, 500 Гц это раз в 2 мс, 1000 Гц это раз в 1 мс, а 8000 Гц это один опрос каждые 125 мкс, то есть каждый микрофрейм.

Фрейм у full-speed шины длится одну миллисекунду, у high-speed та же миллисекунда делится на восемь микрофреймов по 125 мкс. Устройство класса full-speed физически не может отвечать чаще одного раза за фрейм, поэтому потолок для классических геймпадов и дешёвых мышей это 1000 Гц, и вретёнка выше этого никакой драйвер не выжмет. Восемь тысяч герц требуют устройства, подключённого как high-speed, с соответствующим дескриптором и прошивкой, которая успевает готовить свежий отчёт сенсора к каждому микрофрейму. Если сенсор внутри мыши сам дискретизирует движение медленнее, чем его спрашивают, хост получит либо повтор старого отчёта, либо пустой NAK. Пустой ответ тоже стоит времени шины, только пользы не несёт.

Важно понимать, что опрос это не "устройство шлёт, когда хочет". Хост строит расписание транзакций заранее, программируя контроллер на последовательность IN-токенов. Даже когда пользователь не трогает мышь, контроллер честно спрашивает её восемь тысяч раз в секунду и получает NAK. Сама транзакция короткая, но она занимает слот в расписании и генерирует событие завершения в кольцевом буфере контроллера, а это уже касается процессора.

Что происходит в процессоре на каждый отчёт

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

Дальше в Windows включается классическая пара ISR и DPC. Прерывание будит ядро, выполняется короткая процедура обслуживания, которая квитирует событие и ставит отложенный вызов процедуры. DPC исполняется на повышенном уровне приоритета, разбирает кольцевой буфер событий, передаёт отчёты вверх по стеку: драйвер xHCI, стек USB, HID-класс, дальше очередь ввода и подсистема сырого ввода. Только после этого игра или приложение видит движение мыши. Каждый такой проход это переключение контекста, работа с разделяемыми структурами, блокировки и копирование. По отдельности дёшево, но восемь тысяч таких проходов в секунду уже заметная строка в профиле машины.

Есть и вторая цена, о которой забывают чаще. Прерывание вытаскивает ядро из C-состояния. Современный процессор, когда простаивает, уводит ядра в глубокий сон с выключенными петлями тактирования, и выход из этого сна занимает не бесплатные ноль наносекунд, а десятки микросекунд до полного разгона плюс энергию на перезапуск. Если каждые 125 мкс прилетает прерывание от мыши, ядро фактически не успевает засыпать глубже поверхностных состояний. На десктопе это пара лишних ватт, на ноутбуке это уже часы автономности, а на серверной конфигурации с десятками ядер это теплопакет. Поэтому инженер, выбирающий рейт, смотрит не только на задержку отчёта, но и на то, что система перестаёт отдыхать.

Почему 1000 Гц и 8000 Гц разные миры

На бумаге разница между 1000 и 8000 герцами это уменьшение шага дискретизации с одной миллисекунды до 125 мкс, то есть восемь раз. На практике весь тракт обработки должен выдерживать этот шаг. Начнём с самого отчёта: типичный отчёт мыши вмещает смещение по осям в целочисленном виде. При восьми тысячах опросов движение, которое на 1000 Гц давало смещение 24, на каждый отчёт приходится по 3 единицы. Шум сенсора, дрожание руки и квантование начинают конкурировать с полезным сигналом, и производителю приходится включать сглаживание в прошивке, а сглаживание это задержка, которой рейт как раз хотели избежать. Отсюда парадокс, который виден на осциллограммах: часть мышей с рейтом 8000 Гц на медленных движениях показывает медианную задержку хуже, чем та же мышь на 1000 Гц, потому что прошивка копит отсчёты для фильтра.

Дальше рендер-конвейер. Шейперы, то есть устройства и программы переназначения вроде ковриков-эмбейлеров, программных слоёв макросов и средств перехвата ввода, а также сами игры с сырым вводом, обрабатывают отчёты пачками на кадр. Если игра работает на 240 кадрах в секунду, кадр длится 4,16 мс, и за кадр при 1000 Гц приходит четыре-пять отчётов, а при 8000 Гц тридцать три. Обработка этих пачек это уже не прерывание, это пользовательский код, который парсит, накапливает и кормит движок. Тридцать три отчёта на кадр на одну мышь плюс клавиатура плюс геймпад даёт заметный объём системной работы в самом приложении, а не только в ядре.

Наконец, потерянные микрофреймы. Когда DPC задерживается, потому что на том же ядре крутится звуковой драйвер или сетевой стек, события в кольцевом буфере контроллера копятся. Контроллер их не теряет, но обрабатывает система их позже, и выигрыш по задержке тает. При исследовании трасс видно, что на 8000 Гц джиттер межотчётных интервалов заметно шире, чем на 1000 Гц: вместо аккуратной сетки 125 мкс реальные метки времени идут кластерами, потому что ядро обслужило пачку из пяти событий одним пробуждением. Для игрока стабильность интервала часто важнее средней частоты, и ровная тысяча герц уверенно бьёт рваные восемь тысяч.

Как измерить нагрузку, а не верить маркетингу

Инструментарий на Windows для этой задачи стандартен. LatencyMon показывает статистику исполнения DPC и ISR по драйверам: если после перевода мыши на высокий рейтинг растёт время исполнения USBXHCI.SYS и HIDCLASS.SYS, значит, цена рейта конкретна и измерима. Важно смотреть не на счётчик загрузки процессора в диспетчере задач, а на время исполнения отложенных вызовов, потому что именно оно влияет на аудио, сеть и джиттер кадров.

Для глубокого анализа берут Xperf из комплекта Windows Performance Toolkit. Захват трассы с флагами DPC, ISR и стека даёт картину по одному ядру за раз: видно, как часто прерывает контроллер, сколько длится каждый DPC, на каком ядре он исполняется и не мешает ли соседям. В WPA строят график плотности DPC по времени и смотрят перцентили, а не среднее. Хорошая методика такая: сначала замер покоя без движения мышью, потом круговые движения десять секунд на 125 Гц, на 1000 Гц и на 8000 Гц, сравнение по девяносто девятому перцентилю времени DPC и по числу пробуждений ядер в секунду.

  1. Настроить одинаковую сцену и зафиксировать частоту кадров, чтобы тракт рендера не портил сравнение.
  2. Снять базовую трассу на 125 Гц как эталон минимальной нагрузки.
  3. Повторить замер на 1000 Гц и на 8000 Гц при одинаковом движении.
  4. Сравнить перцентили DPC, число пробуждений и стабильность интервалов, а не среднюю загрузку.

Отдельно полезно смотреть счётчики самого контроллера через трассу событий USB: сколько транзакций завершилось с NAK, то есть пустых опросов. Если доля NAK при 8000 Гц превышает половину, устройство физически не успевает готовить данные, и рейт существует только в дескрипторе.

Откуда берётся миф о пяти процентах процессора

Цифра "пять процентов CPU на 1000 Гц" гуляет по форумам много лет, и у неё есть родина: старые измерения на двухъядерных машинах эпохи чипсетных контроллеров, где EHCI-стек был тяжелее, а поллинг обслуживался хуже. На одном ядре из двух заметная строка DPC легко выглядела как пять-семь процентов суммарной загрузки. Это число скопировали в обзоры, из обзоров в статьи, а дальше оно живёт само по себе.

Реальность зависит от четырёх вещей. Первое это сам контроллер: у разных xHCI из разных поколений чипсетов разные пороги модерации прерываний и разная цена транзакции. Второе это число ядер: на шестнадцатиядерной машине один поток DPC это доли процента суммарной ёмкости, на четырёхъядерном ноутбуке заметно больше. Третье это C-состояния: если система настроена на агрессивный сон ядер, каждое пробуждение оплачивается дважды, работой и энергией, а если план питания держит ядра в бодрствовании, то же прерывание обходится дешевле, но горячее. Четвёртое это аффинити: Windows старается держать DPC контроллера на фиксированном ядре, и если на этом же ядре живёт аудиодвижок, конфликт виден в джиттере звука, хотя средняя загрузка крошечная.

На современном многоядерном десктопе честные издержки 1000 Гц от одной мыши это, как правило, доли процента одного ядра по времени DPC и почти незаметная прибавка к питанию. Восемь тысяч герц с одной мыши на свежей платформе обычно укладываются в маленькие единицы процентов одного ядра, но именно одного ядра, и если это ядро делит кэш и трафик с рендер-потоком игры, эффект на стабильность кадров ненулевой. Правильный вопрос не "сколько процентов", а "какое ядро, сколько пробуждений и какой девяносто девятый перцентиль".

Где теряются отчёты корневого хаба и тонкие места тракта

Корневой хаб контроллера это не бездонная труба. У xHCI есть лимиты на число конечных точек и на пропускную способность расписания, и внешний хаб ситуацию не исправляет, а усугубляет: транзакции за хабом транслируются через транслятор транзакций, и для full-speed устройств за high-speed хабом вся их работа упирается в общий бюджет микрофреймов. Практическое следствие простое: три устройства с рейтом 1000 Гц и выше на одном хабе это плотное расписание, где линейные задержки и джиттер растут, потому что транзакции выстраиваются в очередь внутри хаба. Хабы не делят прерывания бесплатно, они делят время шины.

Симметрично важно помнить про младший конец. Старые мыши и часть клавиатур это low-speed или full-speed устройства USB 1.1: их фрейм длится миллисекунду, и их потолок 1000 Гц при идеальной прошивке, а чаще реальный предел ниже. Историческая норма 125 Гц родом оттуда же: восемь миллисекунд bInterval появились как безопасный период, который гарантированно влезал в расписание ранних контроллеров OHCI и UHCI и не ссорился с другими устройствами на шине. Эта частота выбрана не потому, что человек не различает быстрее, а потому, что дёшево и совместимо. Только с приходом стека, который уверенно переживал опрос каждую миллисекунду, нормой стали 1000 Гц.

Материнские платы со вторым контроллером на плате дают настоящее разделение: мышь и клавиатуру с высоким рейтом сажают на разные контроллеры, а геймпад с гарнитурой на встроенный чипсетный. Это снимает конфликт на уровне кольцевых буферов и IRQ. Правило оптимизации выглядит так: высокорейтовые устройства напрямую в порты корневых хабов разных контроллеров, всё низкорейтовое через хаб, мониторинг по LatencyMon после каждой перестановки. И ещё одно здравое правило: рейт выбирают под тракт целиком. Если монитор 144 Гц и игра на 200 кадрах, то 1000 Гц это сбалансированная точка, а 8000 Гц оправданы главным образом при очень высоких кадровых частотах, качественной прошивке мыши и свободном ядре под DPC, иначе это плата за цифру на коробке.