J'essaie de limiter les images par seconde dans une boucle qui effectue une vérification d'intersection, en utilisant C++ avec chrono et thread.
Voici mon code:
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
}
Le problème que j'ai est que toutes les autres sorties de delta affichent des quantités minuscules, plutôt que les ~ 200 ms/trame que je vise:
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
Vous pensez pourquoi cela se produit?
Si vous pensez au fonctionnement de votre code, vous découvrirez qu'il fonctionne exactement comme vous l'avez écrit. Delta oscille en raison d'une erreur logique dans le code.
Voici ce qui se passe:
delta == 0
.200
, Vous codez sleeps 200 - delta(0) == 200
ms.200
(Parce que vous avez mesuré ce temps de sommeil ainsi qu'un travail réel) et vous dormez 200 - delta(200) == 0
ms.Pour résoudre le problème, vous ne devez pas mesurer le temps de sommeil.
Voici comment cela peut être fait:
#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());
}
}
Ce code me donne une séquence régulière 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
Cela ressemble beaucoup à réponse de Galik , mais il conserve la syntaxe de la question de l'OP et ne descend pas dans l'API C. De plus, il crée une unité personnalisée pour la durée du cadre, ce qui, selon moi, est important pour la lisibilité:
#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};
}
}
Cela génère pour moi:
Time: 200ms
Time: 205ms
Time: 205ms
Time: 203ms
Time: 205ms
Time: 205ms
Time: 200ms
Time: 200ms
Time: 200ms
...
Éléments clés à noter:
using frames = duration<int64_t, ratio<1, 5>>;
sleep_until
Au lieu de sleep_for
, Ce qui prend en compte l'inconnu du temps qu'il faut pour faire le vrai travail..count()
sauf pour les E/S et voici une bibliothèque pour s'en débarrasser ./ 1000
).Avec l'ajout de la bibliothèque d'E/S de durée , voici comment le code ci-dessus serait modifié:
#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 sortie serait différente selon la plate-forme (selon la "durée native" de system_clock
). Sur ma plateforme, cela ressemble à ceci:
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
...
Je fais habituellement quelque chose comme ça:
#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);
}
}
Sortie: (cinq pour chaque seconde valeur)
1470173964
1470173964
1470173964
1470173964
1470173964
1470173965
1470173965
1470173965
1470173965
1470173965
1470173966
1470173966
1470173966
1470173966
1470173966
Les temps de delta alternés proviennent d'un problème logique: vous ajoutez un retard à une trame en fonction de la durée de la trame précédente (en termes de calcul des durées de trame). Cela signifie qu'après une longue trame (~ 200 ms), vous n'appliquez pas de retard et obtenez une trame courte (quelques ms), qui déclenche alors un retard sur la trame suivante donnant une trame longue, et ainsi de suite.