Поделиться через


Расписание игры и многоядерный процессор

Поскольку технологии управления питанием становятся все более распространенным явлением на современных компьютерах, часто используемый метод получения времени ЦП с высоким разрешением, инструкция RDTSC, может работать не так, как ожидалось. В этой статье предлагается более точное и надежное решение для получения времени ЦП с высоким разрешением с помощью ИНТЕРФЕЙСов API Windows QueryPerformanceCounter и QueryPerformanceFrequency.

История

С момента появления набора инструкций x86 P5 многие разработчики игр использовали счетчик меток времени чтения ( инструкцию RDTSC) для выполнения времени с высоким разрешением. Мультимедийные таймеры Windows достаточно точны для обработки звука и видео, но при частоте кадров в десятки миллисекунд или меньше они не имеют достаточного разрешения для предоставления информации о разностном времени. Многие игры по-прежнему используют мультимедийный таймер при запуске, чтобы установить частоту ЦП, и они используют это значение частоты для масштабирования результатов из RDTSC для получения точного времени. Из-за ограничений RDTSC API Windows предоставляет более правильный способ доступа к этой функции с помощью подпрограмм QueryPerformanceCounter и QueryPerformanceFrequency.

Такое использование RDTSC для времени страдает от следующих фундаментальных проблем:

  • Неоднородные значения. При использовании RDTSC напрямую предполагается, что поток всегда выполняется на одном процессоре. Многопроцессорные и двухъядерные системы не гарантируют синхронизацию счетчиков циклов между ядрами. Это усугубляется в сочетании с современными технологиями управления питанием, которые простаивают и восстанавливают различные ядра в разное время, что приводит к тому, что ядра обычно не синхронизируются. Для приложения это обычно приводит к сбоям или потенциальным сбоям, так как поток перескакивает между процессорами и получает значения времени, что приводит к большим разностям, отрицательным разностям или остановке времени.
  • Доступность выделенного оборудования. RDTSC блокирует сведения о времени, запрашиваемые приложением счетчику циклов процессора. На протяжении многих лет это был лучший способ получения высокоточной информации о времени, но новые системные платы теперь содержат специальные устройства синхронизации, которые предоставляют информацию о времени с высоким разрешением без недостатков RDTSC.
  • Вариативность частоты ЦП. Часто делается предположение, что частота ЦП фиксирована в течение всего времени существования программы. Однако в современных технологиях управления питанием это неверное предположение. Хотя изначально ограничено ноутбуками и другими мобильными устройствами, технология, которая изменяет частоту ЦП, используется на многих высококлассных настольных пк; Отключение его функции для поддержания согласованной частоты, как правило, неприемлемо для пользователей.

Рекомендации

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

  1. Используйте QueryPerformanceCounter и QueryPerformanceFrequency вместо RDTSC. Эти API могут использовать RDTSC, но вместо этого могут использовать устройства синхронизации на системной плате или некоторые другие системные службы, предоставляющие высококачественную информацию о времени с высоким разрешением. Хотя RDTSC работает гораздо быстрее , чем QueryPerformanceCounter, так как последний является вызовом API, это API, который можно вызывать несколько сотен раз для каждого кадра без каких-либо заметных последствий. (Тем не менее, разработчикам следует попытаться как можно меньше вызывать QueryPerformanceCounter , чтобы избежать снижения производительности.)

  2. При вычислении разностных значений значения должны быть зажаты, чтобы гарантировать, что любые ошибки в значениях времени не приводят к сбоям или нестабильным вычислениям, связанным со временем. Диапазон зажимов должен быть от 0 (чтобы избежать отрицательных значений дельта) до некоторого разумного значения на основе минимальной ожидаемой частоты кадров. Зажим, скорее всего, будет полезен при любой отладке приложения, но не забудьте помнить об этом при анализе производительности или запуске игры в неоптимизированном режиме.

  3. Вычисление всех временных интервалов в одном потоке. Вычисление времени для нескольких потоков, например для каждого потока, связанного с конкретным процессором, значительно снижает производительность многоядерных систем.

  4. Задайте для одного потока значение , чтобы он оставался на одном процессоре, используя Windows API SetThreadAffinityMask. Как правило, это main игровой поток. Хотя QueryPerformanceCounter и QueryPerformanceFrequency обычно корректируются для нескольких процессоров, ошибки в BIOS или драйверах могут привести к тому, что эти подпрограммы возвращают разные значения при перемещении потока от одного процессора к другому. Поэтому лучше всего держать поток на одном процессоре.

    Все остальные потоки должны работать без сбора собственных данных таймера. Мы не рекомендуем использовать рабочий поток для вычисления времени, так как это станет узким местом синхронизации. Вместо этого рабочие потоки должны считывать метки времени из потока main, а так как рабочие потоки считывают только метки времени, нет необходимости использовать критические разделы.

  5. Вызовите QueryPerformanceFrequency только один раз, так как частота не изменится во время работы системы.

Совместимость приложений

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

Чтобы создать эту оболочку, скачайте microsoft Application Compatibility Toolkit из раздела Совместимость приложений Windows.

С помощью администратора совместимости, который входит в набор средств, создайте базу данных приложения и связанные исправления. Создайте новый режим совместимости для этой базы данных и выберите исправление совместимости SingleProcAffinity , чтобы принудительно запустить все потоки приложения на одном процессоре или ядре. С помощью программы командной строки Fixpack.exe (также в составе набора средств) можно преобразовать эту базу данных в устанавливаемый пакет для установки, тестирования и распространения.

Инструкции по использованию администратора совместимости см. в документации по набору средств. Синтаксис и примеры использования Fixpack.exe см. в справке командной строки.

Сведения о клиентах см. в следующих база знаний статьях справки и поддержки Майкрософт: