tiempo infinito for ejemplo ciclo bucle c++ chrono

infinito - ¿Cómo limitar FPS en un bucle con C++?



ciclo for (4)

Estoy tratando de limitar los cuadros por segundo en un bucle que está realizando la comprobación de intersecciones, usando C ++ con crono y hilo.

Aquí está mi código:

std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); std::chrono::system_clock::time_point lastFrame = std::chrono::system_clock::now(); while (true) { // Maintain designated frequency of 5 Hz (200 ms per frame) now = std::chrono::system_clock::now(); std::chrono::duration<double, std::milli> delta = now - lastFrame; lastFrame = now; if (delta.count() < 200.0) { std::chrono::duration<double, std::milli> delta_ms(200.0 - delta.count()); auto delta_ms_duration = std::chrono::duration_cast<std::chrono::milliseconds>(delta_ms); std::this_thread::sleep_for(std::chrono::milliseconds(delta_ms_duration.count())); } printf("Time: %f /n", delta.count()); // Perform intersection test }

El problema que tengo es que cada otra salida de delta muestra cantidades minúsculas, en lugar de los ~ 200 ms / fotograma a los que apunto:

Time: 199.253200 Time: 2.067700 Time: 199.420400 Time: 2.408100 Time: 199.494200 Time: 2.306200 Time: 199.586800 Time: 2.253400 Time: 199.864000 Time: 2.156500 Time: 199.293800 Time: 2.075500 Time: 201.787500 Time: 4.426600 Time: 197.304100 Time: 4.530500 Time: 198.457200 Time: 3.482000 Time: 198.365300 Time: 3.415400 Time: 198.467400 Time: 3.595000 Time: 199.730100 Time: 3.373400

¿Alguna idea de por qué esto está sucediendo?


Esto se parece mucho a la respuesta de Galik , pero mantiene la sintaxis de la pregunta del OP y no se reduce a la API de C. Además, crea una unidad personalizada para la duración del cuadro que creo que es importante para la legibilidad:

#include <chrono> #include <cstdint> #include <iostream> #include <thread> int main() { using namespace std; using namespace std::chrono; using frames = duration<int64_t, ratio<1, 5>>; // 5Hz auto nextFrame = system_clock::now(); auto lastFrame = nextFrame - frames{1};; while (true) { // Perform intersection test this_thread::sleep_until(nextFrame); cout << "Time: " // just for monitoring purposes << duration_cast<milliseconds>(system_clock::now() - lastFrame).count() << "ms/n"; lastFrame = nextFrame; nextFrame += frames{1}; } }

Esto me da resultados:

Time: 200ms Time: 205ms Time: 205ms Time: 203ms Time: 205ms Time: 205ms Time: 200ms Time: 200ms Time: 200ms ...

Cosas clave a tener en cuenta:

  • Una forma concisa de documentar 5Hz: using frames = duration<int64_t, ratio<1, 5>>;
  • Uso de sleep_until lugar de sleep_for , que se ocupa de lo desconocido de cuánto tiempo se tarda en realizar el trabajo real.
  • No se usa .count() excepto para E / S, y aquí hay una biblioteca para deshacerse de eso .
  • Sin conversión manual de unidades (ej. / 1000 ).
  • No hay unidades de punto flotante, no hay nada malo en eso.
  • Mínima necesidad de especificar o depender de unidades explícitas.

Con la adición de la biblioteca de E / S de duración , aquí se muestra cómo se cambiaría el código anterior:

#include "chrono_io.h" #include <chrono> #include <cstdint> #include <iostream> #include <thread> int main() { using namespace date; using namespace std; using namespace std::chrono; using frames = duration<int64_t, ratio<1, 5>>; // 5Hz auto nextFrame = system_clock::now(); auto lastFrame = nextFrame - frames{1};; while (true) { // Perform intersection test this_thread::sleep_until(nextFrame); // just for monitoring purposes cout << "Time: " << system_clock::now() - lastFrame << ''/n''; lastFrame = nextFrame; nextFrame += frames{1}; } }

La salida diferiría dependiendo de la plataforma (dependiendo de la "duración nativa" de system_clock ). En mi plataforma se ve así:

Time: 200042µs Time: 205105µs Time: 205107µs Time: 200044µs Time: 205105µs Time: 200120µs Time: 204307µs Time: 205136µs Time: 201978µs ...


Los tiempos delta alternos surgen de un problema lógico: está agregando un retardo a un cuadro en función de la duración del cuadro anterior (en términos de cómo se calculan las duraciones del cuadro). Esto significa que después de un fotograma largo (~ 200 ms) no se aplica un retraso y se obtiene un fotograma corto (algunos ms), lo que a su vez genera un retraso en el siguiente fotograma, dando un fotograma largo, etc.


Normalmente hago algo como esto:

#include <chrono> #include <iostream> int main() { using clock = std::chrono::steady_clock; auto next_frame = clock::now(); while(true) { next_frame += std::chrono::milliseconds(1000 / 5); // 5Hz // do stuff std::cout << std::time(0) << ''/n''; // 5 for each second // wait for end of frame std::this_thread::sleep_until(next_frame); } }

Salida: (cinco por cada segundo valor)

1470173964 1470173964 1470173964 1470173964 1470173964 1470173965 1470173965 1470173965 1470173965 1470173965 1470173966 1470173966 1470173966 1470173966 1470173966


Si piensa en cómo funciona su código, descubrirá que funciona exactamente como lo escribió. Delta oscila debido a un error lógico en el código.

Esto es lo que pasa:

  • Comenzamos con delta == 0 .
  • Debido a que el delta es más pequeño que 200 , el código duerme 200 - delta(0) == 200 ms.
  • Ahora, el delta en sí se acerca a 200 (porque ha medido el tiempo de sueño así como un trabajo real) y duerme 200 - delta(200) == 0 ms.
  • Después de eso se repite el ciclo.

Para solucionar el problema necesitas no medir el tiempo de sueño.

Así es como se puede hacer:

#include <iostream> #include <cstdio> #include <chrono> #include <thread> std::chrono::system_clock::time_point a = std::chrono::system_clock::now(); std::chrono::system_clock::time_point b = std::chrono::system_clock::now(); int main() { while (true) { // Maintain designated frequency of 5 Hz (200 ms per frame) a = std::chrono::system_clock::now(); std::chrono::duration<double, std::milli> work_time = a - b; if (work_time.count() < 200.0) { std::chrono::duration<double, std::milli> delta_ms(200.0 - work_time.count()); auto delta_ms_duration = std::chrono::duration_cast<std::chrono::milliseconds>(delta_ms); std::this_thread::sleep_for(std::chrono::milliseconds(delta_ms_duration.count())); } b = std::chrono::system_clock::now(); std::chrono::duration<double, std::milli> sleep_time = b - a; // Your code here printf("Time: %f /n", (work_time + sleep_time).count()); } }

Este código me da una secuencia constante de deltas:

Time: 199.057206 Time: 199.053581 Time: 199.064718 Time: 199.053515 Time: 199.053307 Time: 199.053415 Time: 199.053164 Time: 199.053511 Time: 199.053280 Time: 199.053283