109 votes

Chronométrage à la microseconde en JavaScript

Existe-t-il des fonctions de chronométrage en JavaScript avec une résolution de l'ordre de la microseconde ?

Je suis conscient de timer.js pour Chrome, et j'espère qu'il y aura une solution pour les autres navigateurs conviviaux, comme Firefox, Safari, Opera, Epiphany, Konqueror, etc. Je ne suis pas intéressé par le support de tout IE, mais les réponses y compris Les IE sont les bienvenus.

(Étant donné la faible précision de la synchronisation en millisecondes dans JS, je ne retiens pas mon souffle sur ce point).

Mise à jour : timer.js annonce une résolution en microsecondes, mais il multiplie simplement la lecture en millisecondes par 1 000. Vérifié par des tests et une inspection du code. Déçu :[

150voto

NicJ Points 1018

Comme indiqué dans la réponse de Mark Rejhon, il existe une API disponible dans les navigateurs modernes qui expose des données de synchronisation de résolution inférieure à la milliseconde à script : la Minuterie haute résolution du W3C , alias window.performance.now() .

now() est meilleur que le traditionnel Date.getTime() de deux façons importantes :

  1. now() est un double à résolution submilliseconde qui représente le nombre de millisecondes depuis le début de la navigation de la page. Il renvoie le nombre de microsecondes dans la fraction (par exemple, une valeur de 1000.123 correspond à 1 seconde et 123 microsecondes).

  2. now() est monotone et croissante. Ceci est important car Date.getTime() peut éventuellement sauter en avant ou même en arrière lors des appels suivants. Notamment, si l'heure du système d'exploitation est mise à jour (par exemple, synchronisation de l'horloge atomique), Date.getTime() est également mis à jour. now() est garanti comme étant toujours monotone et croissant, donc il n'est pas affecté par l'heure du système d'exploitation -- il sera toujours à l'heure de l'horloge murale (en supposant que votre horloge murale n'est pas atomique...).

now() peut être utilisé dans presque tous les endroits où new Date.getTime() , + new Date et Date.now() sont. L'exception est que Date et now() les temps ne se mélangent pas, comme Date est basé sur unix-epoch (le nombre de millisecondes depuis 1970), alors que now() est le nombre de millisecondes depuis le début de la navigation de votre page (il sera donc beaucoup plus petit que Date ).

now() est pris en charge dans Chrome stable, Firefox 15+ et IE10. Il existe également plusieurs polyfills disponible.

Note : Lorsque vous utilisez Travailleurs du Web le window n'est pas disponible, mais vous pouvez toujours utiliser la variable performance.now() .

21voto

Mark Rejhon Points 441

Il existe désormais une nouvelle méthode pour mesurer les microsecondes en javascript : http://gent.ilcore.com/2012/06/better-timer-for-javascript.html

Cependant, dans le passé, j'ai trouvé une méthode rudimentaire pour obtenir une précision de 0,1 milliseconde en JavaScript à partir d'un minuteur de millisecondes. Impossible ? Non, pas du tout. Continuez à lire :

Je fais des expériences de haute précision qui nécessitent des minuteries autocontrôlées, et j'ai constaté que je pouvais obtenir une précision de 0,1 milliseconde avec certains navigateurs sur certains systèmes.

J'ai constaté que dans les navigateurs Web modernes accélérés par le GPU sur des systèmes rapides (par exemple i7 quad core, où plusieurs cœurs sont inactifs, seule la fenêtre du navigateur) - je peux maintenant faire confiance aux minuteurs pour être précis à la milliseconde près. En fait, c'est devenu si précis sur un système i7 inactif que j'ai pu obtenir de manière fiable la même milliseconde exacte, sur plus de 1 000 tentatives. Ce n'est que lorsque j'essaie de faire des choses comme charger une page web supplémentaire, ou autre, que la précision en millisecondes se dégrade (et je suis capable d'attraper avec succès ma propre précision dégradée en faisant une vérification du temps avant et après, pour voir si mon temps de traitement s'est soudainement allongé à 1 ou plusieurs millisecondes - cela m'aide à invalider les résultats qui ont probablement été trop affectés par les fluctuations du CPU).

Il est devenu si précis dans certains navigateurs accélérés par le GPU sur des systèmes i7 quad-core (lorsque la fenêtre du navigateur est la seule fenêtre), que j'en suis venu à souhaiter pouvoir accéder à une minuterie d'une précision de 0,1 ms en JavaScript, puisque la précision est enfin présente sur certains systèmes de navigation haut de gamme pour rendre cette précision de minuterie intéressante pour certains types d'applications de niche qui nécessitent une haute précision, et où les applications sont capables d'auto-vérifier les écarts de précision.

Il est évident que si vous effectuez plusieurs passages, vous pouvez simplement effectuer plusieurs passages (par exemple 10 passages) puis diviser par 10 pour obtenir une précision de 0,1 milliseconde. Il s'agit d'une méthode courante pour obtenir une meilleure précision - effectuer plusieurs passes et diviser le temps total par le nombre de passes.

CEPENDANT... Si je ne peux faire qu'une seule passe de référence d'un test spécifique en raison d'une situation inhabituelle, j'ai découvert que je peux obtenir une précision de 0,1 (et parfois 0,01ms) en faisant cela :

Initialisation/Calibrage :

  1. Exécuter une boucle d'occupation pour attendre que la minuterie passe à la milliseconde suivante (aligner la minuterie sur le début de l'intervalle de la milliseconde suivante) Cette boucle d'occupation dure moins d'une milliseconde.
  2. Exécutez une autre boucle occupée pour incrémenter un compteur en attendant que le timer s'incrémente. Le compteur vous indique combien d'incréments de compteur ont eu lieu en une milliseconde. Cette boucle occupée dure une milliseconde complète.
  3. Répétez ce qui précède, jusqu'à ce que les chiffres deviennent ultra-stables (temps de chargement, compilateur JIT, etc.). 4. NOTE : La stabilité du nombre vous donne votre précision atteignable sur un système inactif. Vous pouvez calculer la variance, si vous avez besoin de vérifier la précision. Les variances sont plus grandes sur certains navigateurs, et plus petites sur d'autres navigateurs. Elles sont plus grandes sur les systèmes rapides et plus lentes sur les systèmes lents. La cohérence varie également. Vous pouvez déterminer quels navigateurs sont plus cohérents/précis que d'autres. Les systèmes plus lents et les systèmes occupés entraîneront des écarts plus importants entre les passes d'initialisation. Cela peut vous donner l'occasion d'afficher un message d'avertissement si le navigateur ne vous donne pas suffisamment de précision pour permettre des mesures de 0,1ms ou 0,01ms. Le décalage des minuteries peut être un problème, mais certaines minuteries en millisecondes entières sur certains systèmes s'incrémentent de manière assez précise (tout à fait sur le point), ce qui donnera des valeurs d'étalonnage très cohérentes auxquelles vous pouvez faire confiance.
  4. Sauvegarder la valeur finale du compteur (ou la moyenne des dernières passes d'étalonnage).

Évaluation comparative d'un passage avec une précision inférieure à la milliseconde :

  1. Exécutez une boucle occupée pour attendre que la minuterie passe à la milliseconde suivante (alignez la minuterie sur le début de l'intervalle de la milliseconde suivante). Cette boucle occupée dure moins d'une milliseconde.
  2. Exécutez la tâche que vous voulez pour évaluer précisément le temps.
  3. Vérifiez la minuterie. Cela vous donne le nombre entier de millisecondes.
  4. Exécutez une boucle finale occupée pour incrémenter un compteur en attendant l'incrémentation de la minuterie. Cette boucle occupée dure moins d'une milliseconde.
  5. Divisez cette valeur de compteur, par la valeur de compteur originale de l'initialisation.
  6. Maintenant vous avez la partie décimale des millisecondes !!!!!!!!

AVERTISSEMENT : Les boucles d'activité ne sont PAS recommandées dans les navigateurs Web, mais heureusement, ces boucles d'activité durent moins d'une milliseconde chacune et ne sont exécutées qu'un très petit nombre de fois.

Des variables telles que la compilation JIT et les fluctuations du CPU ajoutent des inexactitudes massives, mais si vous exécutez plusieurs passes d'initialisation, vous aurez une recompilation dynamique complète, et finalement le compteur se stabilise à quelque chose de très précis. Assurez-vous que toutes les boucles occupées sont exactement la même fonction dans tous les cas, de sorte que les différences dans les boucles occupées n'entraînent pas de différences. Assurez-vous que toutes les lignes de code sont exécutées plusieurs fois avant de commencer à faire confiance aux résultats, pour permettre aux compilateurs JIT de s'être déjà stabilisés à une recompilation dynamique complète (dynarec).

En fait, j'ai été témoin d'une précision approchant les microsecondes sur certains mais je ne lui ferais pas encore confiance. Mais la précision de 0,1 milliseconde semble fonctionner de manière assez fiable, sur un système quad-core inactif où je suis la seule page du navigateur. Je suis arrivé à un cas de test scientifique où je ne pouvais faire que des passes uniques (en raison de variables uniques), et j'avais besoin de chronométrer précisément chaque passe, plutôt que de faire la moyenne de plusieurs passes répétées, c'est pourquoi j'ai fait cela.

J'ai fait plusieurs pré-passes et passes fictives (aussi pour régler le dynarec), pour vérifier la fiabilité de la précision de 0.1ms (restée solide pendant plusieurs secondes), puis j'ai gardé mes mains hors du clavier/souris, pendant que le benchmark se produisait, puis j'ai fait plusieurs post-passes pour vérifier la fiabilité de la précision de 0.1ms (restée solide à nouveau). Cela permet également de vérifier que des choses telles que des changements d'état d'alimentation, ou d'autres choses, ne se sont pas produites entre l'avant et l'après, interférant avec les résultats. Répétez le pré-test et le post-test entre chaque passage du benchmark. De cette manière, j'étais pratiquement certain que les résultats intermédiaires étaient exacts. Il n'y a pas de garantie, bien sûr, mais cela montre qu'une précision de <0,1ms est possible dans le cadre d'un projet de recherche. un peu de dans un navigateur web.

Cette méthode n'est utile que dans les cas très, très niche cas. Même ainsi, il ne sera littéralement pas garanti à l'infini à 100%, vous pouvez obtenir une précision tout à fait digne de confiance, et même une précision scientifique lorsqu'elle est combinée à plusieurs couches de vérifications internes et externes.

3voto

agm1984 Points 3539

Voici un exemple montrant mon minuteur haute résolution pour node.js :

 function startTimer() {
   const time = process.hrtime();
   return time;
 }

 function endTimer(time) {
   function roundTo(decimalPlaces, numberToRound) {
     return +(Math.round(numberToRound + `e+${decimalPlaces}`)  + `e-${decimalPlaces}`);
   }
   const diff = process.hrtime(time);
   const NS_PER_SEC = 1e9;
   const result = (diff[0] * NS_PER_SEC + diff[1]); // Result in Nanoseconds
   const elapsed = result * 0.0000010;
   return roundTo(6, elapsed); // Result in milliseconds
 }

Utilisation :

 const start = startTimer();

 console.log('test');

 console.log(`Time since start: ${endTimer(start)} ms`);

Normalement, vous pourriez être en mesure d'utiliser :

 console.time('Time since start');

 console.log('test');

 console.timeEnd('Time since start');

Si vous chronométrez des sections de code qui impliquent des boucles, vous ne pouvez pas accéder à la valeur de la fonction console.timeEnd() afin d'additionner les résultats de vos minuteurs. Vous pouvez le faire, mais cela devient désagréable car vous devez injecter la valeur de votre variable itérative, comme par exemple i et définir une condition pour détecter si la boucle est terminée.

Voici un exemple, car il peut être utile :

 const num = 10;

 console.time(`Time til ${num}`);

 for (let i = 0; i < num; i++) {
   console.log('test');
   if ((i+1) === num) { console.timeEnd(`Time til ${num}`); }
   console.log('...additional steps');
 }

Citer : https://nodejs.org/api/process.html#process_process_hrtime_time

2voto

Pointy Points 172438

La réponse est "non", en général. Si vous utilisez JavaScript dans un environnement côté serveur (c'est-à-dire, pas dans un navigateur), alors tout est permis et vous pouvez essayer de faire ce que vous voulez.

modifier - Cette réponse est ancienne ; les normes ont progressé et des installations plus récentes sont disponibles comme solutions au problème de l'heure exacte. Malgré tout, il faut se rappeler qu'en dehors du domaine d'un véritable système d'exploitation en temps réel, le code ordinaire non privilégié a un contrôle limité sur son accès aux ressources de calcul. Mesurer les performances n'est pas (nécessairement) la même chose que prédire performance.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X