Performances Web : lorsque la milliseconde n'offre plus assez de précision

Dans certains cas, les mesures de temps basées sur une résolution à la milliseconde près ne sont pas assez précises. Aux côtés de grands acteurs du secteur et de responsables de la communauté, le groupe de travail Web Performance du W3C a cherché à résoudre ce problème en standardisant la spécification High Resolution Time (Temps haute résolution). Cette semaine, la spécification a été publiée en tant que Proposed Recommendation (PR, recommandation proposée) et son adoption dans les navigateurs récents s'accélère. Pour mieux comprendre comment fonctionne cette API, examinez la démonstration What Time is it?.

En seulement huit mois, la spécification est passée du simple statut d'idée à celui de Proposed Recommendation (PR). L'étape PR de la standardisation est la dernière étape d'un standard Web avant son passage en recommandation W3C officielle. Par ailleurs, cette interface a été largement adoptée dans les navigateurs. Elle est intégralement prise en charge dans Internet Explorer 10 et Firefox 15, et prise en charge à travers un préfixe dans Chrome 22. Voilà un bel exemple de ce qu'il est possible de faire lorsque les grands acteurs du secteur et la communauté mettent leurs forces en commun par l'entremise du W3C.

Revenons au sujet initial : pourquoi la milliseconde ne suffit-elle plus ? Depuis longtemps, le temps est mesuré dans la plateforme Web par le biais d'un objet Date JavaScript, à l'aide de la méthode Date.now() ou du type DOMTimeStamp. Pour représenter le temps, l'objet Date exprime la durée écoulée depuis le 1er janvier 1970 (heure UTC), en millisecondes. Dans la plupart des situations, cette définition du temps s'avère suffisante pour représenter n'importe quel point dans le temps pendant 285 616 ans à partir du 1er janvier 1970 (heure UTC).

Par exemple, à l'heure où j'écris ce billet de blog, la valeur de temps Date.now() indiquée dans ma console Outils de développement d'IE10 est 1350509874902. Ce numéro à treize chiffres représente le nombre de millisecondes écoulées depuis l'origine de cette base temporelle, c'est-à-dire depuis le 1er janvier 1970. Cette heure correspond au 17 octobre 2012, à 21 h 37 et 54 secondes (heure UTC).

Si cette définition reste très utile pour déterminer la date et l'heure actuelle, elle montre cependant ses limites dans certaines situations. Par exemple, il peut être utile pour les développeurs de déterminer si leur animation ne subit aucune saccade à 60 images par seconde (une image toutes les 16,667 millisecondes). À l'aide de la méthode simple permettant de calculer le nombre d'images instantané, c'est-à-dire en déterminant le moment du dernier rappel de tracé d'image, nous pouvons déduire uniquement que le nombre d'images par seconde est compris entre 58,8 (1/17) et 62,5 (1/16).

De même, une résolution inférieure à la milliseconde est souhaitable pour mesurer avec précision le temps écoulé (par exemple en utilisant les API Navigation Timing, Resource Timing et User Timing pour instrumenter la synchronisation de votre réseau et de vos scripts) ou pour essayer de synchroniser des scènes d'animation ou des données audio avec une animation.

Pour résoudre ce problème, la spécification High Resolution Time définit une nouvelle base temporelle utilisant au minimum une résolution à la microseconde près (un millième de milliseconde). Pour réduire le nombre de bits utilisés pour représenter cette valeur et améliorer la lisibilité, au lieu de mesurer le temps écoulé depuis le 1er janvier 1970 UTC, cette nouvelle base temporelle mesure le temps écoulé depuis le début de la navigation sur le document, performance.timing.navigationStart.

La spécification définit performance.now() en tant que méthode analogue à Date.now(), pour déterminer l'heure actuelle en haute résolution. DOMHighResTimeStamp est le type analogue de DOMTimeStamp, qui définit la valeur temporelle à haute résolution.

Par exemple, en consultant l'heure actuelle à l'aide de performance.now() et de Date.now() dans la console des Outils de développement d'IE10 lors de la rédaction de ce blog, je vois les deux valeurs suivantes :

 performance.now():        196.304879519774
Date.now():        1350509874902

Même si ces deux valeurs temporelles représentent le même point dans le temps, elles sont mesurées à partir d'une origine différente. La valeur performance.now() s'avère bien plus lisible.

Comme le temps haute résolution est mesuré à partir du début de la navigation sur le document, la valeur performance.now() dans un sous-document est mesurée à partir du début de la navigation sur le sous-document et non pas sur le document racine. Imaginons par exemple qu'un document possède un iframe A same-origine et un iframe B cross-origin. La navigation sur l'iframe A a commencé environ cinq millisecondes après le début de la navigation sur le document racine et la navigation sur l'iframe B environ 10 millisecondes après le début de la navigation sur le document racine. Si nous mesurons l'instant exact situé 15 millisecondes après le début de la navigation sur le document racine, nous obtenons les valeurs suivantes pour les appels de performance.now() dans les différents contextes :

performance.now() dans l'iframe B :                                5,123 ms

performance.now() dans l'iframe A :                               10,123 ms

performance.now() dans le document racine :                    15,123 ms

Date.now() dans n'importe quel contexte :                         134639846051 ms

Schéma illustrant les différences entre les mesures de Date.now() et de performance.now()
Schéma : Date.now() correspond au temps écoulé depuis le 1er janvier 1970, tandis que performance.now() correspond au temps écoulé depuis le début de la navigation sur le document.

Cette approche permet non seulement d'empêcher toute fuite de données au moment de la création du parent dans les iframes cross-origin, mais aussi de mesurer le temps par rapport à votre point de départ. Par exemple, si vous utilisez l'interface Resource Timing (qui utilise la spécification High Resolution Time) pour déterminer le temps mis par un serveur pour répondre à une requête de ressource dans un sous-document, il n'est pas nécessaire d'apporter des modifications pour prendre en compte le temps nécessaire à l'ajout du sous-document au document racine.

Si vous souhaitez effectuer des comparaisons temporelles d'une image à l'autre, il vous suffit d'interroger top.performance.now() pour obtenir une valeur de temps par rapport au début de la navigation sur le document racine. La valeur renvoyée est la même dans tous les iframes same-origin.

Par rapport à Date.now(), l'API performance.now() offre aussi l'avantage de voir sa valeur augmenter de façon monotone et d'être insensible aux déviations et aux ajustements d'horloge. La différence entre les appels suivants de performance.now() ne sera jamais négative. Date.now() n'offre pas ce type de garantie, et en pratique des cas de temps négatifs ont été signalés dans les données d'analyse.

La spécification High Resolution Time est un autre exemple qui montre bien à quelle vitesse les idées innovantes peuvent devenir des standards interopérables sur lesquels les développeurs peuvent s'appuyer au sein des navigateurs HTML5 modernes. Merci à tous les participants au groupe de travail Web Performance du W3C pour avoir contribué à la réalisation de cette API, ainsi qu'aux autres éditeurs de navigateurs pour avoir implémenté rapidement ces API, en vue de progresser sur la voie de l'interopérabilité.

Avec nos remerciements,
Jatinder Mann
Chef de projet Internet Explorer