Что хорошего в изменениях маски сходства потоков для текущего потока?

Я пишу игровой движок, и мне нужен способ получить точное и точное значение "deltatime", из которого можно получить текущий FPS для отладки, а также ограничить частоту кадров (это важно для нашего проекта).

Проведя небольшое исследование, я обнаружил, что один из лучших способов сделать это - использовать WinAPI. QueryPerformanceCounter функция. GetTicksCount должен использоваться для предотвращения встречных скачков, но сам по себе он не очень точен.

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

  1. Может ли ОС "перераспределить" поток на другое ядро, когда поток уже запущен, или поток выделен для данного ядра, и так до тех пор, пока поток не умрет?
  2. Если поток не может быть перераспределен (по крайней мере, для меня это имеет большой смысл), то почему я могу сделать что-то вроде SetThreadAffinityMask(GetCurrentThread(),mask)? Ogre3D делает это в своем классе Ogre::Timer (реализация Windows), и я предполагаю, что это позволит избежать возврата во времени. Но для того, чтобы это было правдой, я должен был бы рассмотреть возможность произвольного перемещения потоков из одного ядра в другое ОС, что мне кажется довольно странным (не знаю почему).

Я думаю, что это было все, что я хотел знать на данный момент. Благодарю.

6 ответов

Решение

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

Что касается таймера apis. TimeGetTime предназначен для мультимедийного времени, поэтому он немного точнее, чем GetTickCount,

QueryPerformanceCounter(). все еще ваше самое точное измерение, хотя. У Microsoft есть это, чтобы сказать об этом.

На многопроцессорном компьютере не должно иметь значения, какой процессор называется. Однако вы можете получить разные результаты на разных процессорах из-за ошибок в базовой системе ввода / вывода (BIOS) или на уровне аппаратной абстракции (HAL). Чтобы указать привязку процессора к потоку, используйте функцию SetThreadAffinityMask.

Поэтому, если вы проводите временные тесты на определенном компьютере, вам может не понадобиться беспокоиться о том, что QPC пойдет в обратном направлении, вам следует провести некоторое тестирование и посмотреть, имеет ли это значение на вашей машине.

Потоки могут быть (и если они не имеют установленного сродства) перераспределены во время работы потока. Windows распределяет нагрузку на все процессоры, чтобы максимизировать производительность.

Даже если вы заблокируете поток на одном процессоре, используя SetAffinityMask, QPC может работать в обратном направлении, если вам действительно не повезло, а аппаратное обеспечение отстой. Лучше просто разобраться со случаем возврата QPC плохих значений. В Windows 7 QPC был значительно улучшен в этом отношении, но, поскольку вы пишете игру, вы, вероятно, ориентируетесь на XP, где она вам не поможет.

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

1) Поток может выделить поток для любого ядра, имеющего свободное время обработки. Именно поэтому вы часто будете видеть программное обеспечение, использующее 50% на четырехъядерном компьютере, но при проверке графиков оно использует половину всех четырех.

2) Смотри 1;)

Использование SetThreadAffinity() обычно является плохой идеей, за исключением случая, когда поток выполняет только синхронизацию. Если вы привязываете свой поток к одному ядру, вы в первую очередь устраняете все преимущества многоядерной системы. Ваше приложение больше не может масштабироваться. Даже если вы запустите несколько экземпляров своего приложения, они все равно будут привязаны к одному ядру.

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

Одна вещь, которая делает его немного легче, состоит в том, что наш движок разделен на широкие компоненты, которые всегда работают одновременно (например, игровой / логический "сервер", ввод / графика "клиент", аудио, рендеринг - каждый их собственный поток), так что мы делаем это - блокируем каждый из этих потоков на свое собственное ядро ​​и независимо синхронизируем их.

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

Другие вопросы по тегам