std :: this_thread :: sleep_for спит слишком долго - PullRequest
2 голосов
/ 17 октября 2019

Может кто-нибудь сказать, в чем проблема в следующем примере?

Производит 65 вместо 300 кадров в секунду.

#define WIN32_LEAN_AND_MEAN

#include <Windows.h>

#include <Thread>
#include <Chrono>
#include <String>

int main(int argc, const char* argv[]) {

    using namespace std::chrono_literals;

    constexpr unsigned short FPS_Limit = 300;

    std::chrono::duration<double, std::ratio<1, FPS_Limit>> FrameDelay = std::chrono::duration<double, std::ratio<1, FPS_Limit>>(1.0f);

    unsigned int FPS = 0;

    std::chrono::steady_clock SecondTimer;
    std::chrono::steady_clock ProcessTimer;

    std::chrono::steady_clock::time_point TpS = SecondTimer.now();
    std::chrono::steady_clock::time_point TpP = ProcessTimer.now();

    while (true) {

        // ...

        // Count FPS

        FPS++;

        if ((TpS + (SecondTimer.now() - TpS)) > (TpS + 1s)) {

            OutputDebugString(std::to_string(FPS).c_str()); OutputDebugString("\n");

            FPS = 0;

            TpS = SecondTimer.now();
        }

        // Sleep

        std::this_thread::sleep_for(FrameDelay - (ProcessTimer.now() - TpP));    // FrameDelay minus time needed to execute other things

        TpP = ProcessTimer.now();
    }

    return 0;
}

Я думаю, это как-то связано с std::chrono::duration<double, std::ratio<1, FPS_Limit>>, но когда оно умножается на FPS_Limit правильные 1 кадрыв секунду.

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

1 Ответ

2 голосов
/ 17 октября 2019

Короче говоря, проблема в том, что вы вообще используете std::this_thread::sleep_for. Или любой "сон" в этом отношении. Спать, чтобы ограничить частоту кадров, совершенно неправильно.

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

Что делает std::this_thread::sleep_for (дать или принять несколько строк проверок работоспособности и проверки ошибок),он вызывает функцию Win32 Sleep (или, в другой ОС, другую похожую функцию, такую ​​как nanosleep).

Теперь, что делает Sleep? Где-то в маленькой красной книге операционной системы он отмечает, что ваш поток должен быть снова подготовлен в будущем, а затем отображает ваш поток not-ready . Быть не готовым означает просто, что ваш поток не находится в списке кандидатов, которые будут запланированы для получения процессорного времени.

Иногда, в конце концов, аппаратный таймер запускает прерывание. Это может быть периодический таймер (до Windows 8) с ужасно плохим разрешением по умолчанию или программируемое прерывание по одному кадру, что угодно. Вы даже можете настроить разрешение этого таймера, но это - глобальная вещь 1017 *, которая значительно увеличивает количество переключений контекста. Плюс, это не решает актуальную проблему . Когда ОС обрабатывает прерывание, она ищет в своей книге информацию о том, какие потоки необходимо подготовить, и делает это.

Это, однако, не то же самое, что запуск вашегонить. Это всего лишь кандидатура для повторного запуска (может быть, когда-нибудь).

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

Решение. Не спите. Включите вертикальную синхронизацию или предоставьте ее пользователю.

...