Interpréter les performances de votre application Web en conditions réelles dans IE11 et dans d'autres navigateurs

Aux côtés de Google, de Mozilla et d'autres acteurs importants dans le domaine des navigateurs, le groupe de travail Web Performance du W3C a standardisé les interfaces Navigation Timing, Resource Timing, User Timing et Performance Timeline pour vous permettre de mieux comprendre les performances des opérations de navigation, de récupération des ressources et d'exécution de scripts dans votre application Web en conditions réelles. Vous pouvez utiliser ces interfaces pour collecter des informations permettant d'analyser la manière dont les clients utilisent votre application Web en conditions réelles, au lieu de vous appuyer sur des tests purement synthétiques, qui mesurent les performances de votre application dans un environnement artificiel. Grâce à ces données de minutage, vous pouvez identifier les éléments à améliorer pour optimiser les performances de vos applications Web en conditions réelles. Toutes ces interfaces sont prises en charge dans IE11. Pour voir comment ces interfaces fonctionnent en pratique, testez la démonstration Test Drive Performance Timing.

La démonstration Test Drive Performance Timing vous permet de tester les API de minutage.
La démonstration Test Drive Performance Timing vous permet de tester les API de minutage.

Performance Timeline

La spécification Performance Timeline, publiée sous forme de recommandation W3C, est entièrement prise en charge dans IE11 et Chrome 30. Grâce à cette interface, vous profitez d'un point de vue exhaustif sur le temps consacré à la navigation, à la récupération des ressources et à l'exécution des scripts en cours d'exécution dans votre application. Cette spécification définit à la fois les attributs minimum que les métriques de performances doivent implémenter et les interfaces que les développeurs peuvent utiliser pour récupérer n'importe quel type de métrique de performances.

Toutes les métriques de performances doivent prendre en charge les quatre attributs suivants :

  • name. Cet attribut stocke un identificateur unique pour la métrique de performances. Pour une ressource, par exemple, il s'agit de l'URL résolue de la ressource.
  • entryType. Cet attribut stocke le type de métrique de performances. La métrique d'une ressource sera par exemple stockée en tant que « resource ».
  • startTime. Cet attribut stocke le premier timestamp enregistré de la métrique de performances.
  • duration. Cet attribut stocke la durée de bout en bout de l'événement enregistré par la métrique.

L'ensemble des données de minutage sont enregistrées sous forme de temps haute résolution à l'aide du type DOMHighResTimeStamps, défini dans la spécification High Resolution Time. Contrairement à DOMTimeStamps, qui mesure les valeurs de temps en millisecondes à partir du 1er janvier 1970 (heure UTC), la valeur de temps haute résolution est mesurée au moins à la microseconde près à partir du début de la navigation dans le document. Par exemple, si je consulte l'heure actuelle en utilisant performance.now(), l'heure haute résolution correspondant à Date.now(), j'obtiens l'interprétation suivante de l'heure actuelle :

> performance.now();

4038.2319370044793

 

> Date.now()

1386797626201

Cette valeur de temps offre également l'avantage de ne pas être affectée par les phénomènes de dérive d'horloge ni par les ajustements. Vous pouvez essayer la démonstration Test Drive What Time Is It pour comprendre comment utiliser le temps haute résolution.

Vous pouvez utiliser les interfaces suivantes pour récupérer la liste des métriques de performances enregistrées au moment de l'appel. Grâce à startTime et à duration, ainsi qu'aux autres attributs fournis par la métrique, vous pouvez obtenir une vue chronologique de bout en bout des performances de votre page, telles qu'elles ont été ressenties par vos clients.

PerformanceEntryList getEntries();

PerformanceEntryList getEntriesByType(DOMString entryType);

PerformanceEntryList getEntriesByName(DOMString name, optional DOMString entryType);

La méthode getEntriesrenvoie la liste de toutes les métriques de performances de la page, tandis que les autres méthodes renvoient des éléments spécifiques, en fonction du nom ou du type. Selon nous, la plupart des développeurs utiliseront simplement la fonction JSON stringify sur la liste complète de métriques et enverront les résultats à leur serveur plutôt qu'au client en vue de l'analyse.

Examinons de plus près chaque métrique de performances : navigation, resource, marks et measures.

Navigation Timing

Les interfaces Navigation Timing fournissent à votre application Web des mesures de minutage précises pour chaque phase de navigation. La spécification Navigation Timing L1 a été publiée sous forme de recommandation W3C et est intégralement prise en charge depuis IE9, Chrome 28 et Firefox 23. La spécification Navigation Timing L2 est une première ébauche de travail publique. Elle est prise en charge par IE11.

Grâce à Navigation Timing, les développeurs obtiennent non seulement le temps précis de chargement de la page de bout en bout (en prenant en compte le temps nécessaire pour obtenir la page auprès du serveur), mais aussi la répartition du temps consacré à chaque phase de mise en réseau et de traitement du DOM : déchargement, redirection, mise en cache de l'application, DNS, TCP, demande, réponse, traitement du DOM et événement de chargement. Le script ci-dessous utilise Navigation Timing L2 pour accéder à ces informations détaillées. Le type d'entrée de cette métrique est « navigation », tandis que le nom est « document ». Vous pouvez essayer une démonstration de Navigation Timing sur le site IE Test Drive.

<!DOCTYPE html>

<html>

<head></head>

<body>

<script>

 function sendNavigationTiming() {

   var nt = performance.getEntriesByType('navigation')[0];

   var navigation = ' Start Time: ' + nt.startTime + 'ms';

   navigation += ' Duration: ' + nt.duration + 'ms';

   navigation += ' Unload: ' + (nt.unloadEventEnd - nt.unloadEventStart) + 'ms';

   navigation += ' Redirect: ' + (nt.redirectEnd - nt.redirectStart) + 'ms';

   navigation += ' App Cache: ' + (nt. domainLookupStart - nt.fetchStart) + 'ms';

   navigation += ' DNS: ' + (nt.domainLookupEnd - nt.domainLookupStart) + 'ms';

   navigation += ' TCP: ' + (nt.connectEnd - nt.connectStart) + 'ms';

   navigation += ' Request: ' + (nt.responseStart - nt.requestStart) + 'ms';

   navigation += ' Response: ' + (nt.responseEnd - nt.responseStart) + 'ms';

   navigation += ' Processing: ' + (nt.domComplete - nt.domLoading) + 'ms';

   navigation += ' Load Event: ' + (nt.loadEventEnd - nt.loadEventStart) + 'ms';

   sendAnalytics(navigation);

 }

</script>

</body>

</html>

En examinant en détail le temps consacré à chacune des phases réseau, vous pouvez mieux diagnostiquer et corriger les problèmes de performances. Vous pouvez par exemple envisager de ne pas utiliser une redirection si vous trouvez que la durée de la redirection est trop importante, utiliser un service de mise en cache DNS si le temps DNS est trop important, utiliser un CDN plus proche de vos utilisateurs si la durée de la demande est trop importante, ou compresser vos contenus au format Gzip si le temps de réponse est trop long. Pour découvrir des conseils et des astuces sur l'optimisation des performances réseau, consultez cette vidéo.

La principale différence entre les deux versions de la spécification Navigation Timing réside dans le mode d'accès aux données de minutage et dans la méthode de mesure du temps. L'interface L1 définit ces attributs sous l'objet performance.timing, sous la forme d'un nombre de millisecondes écoulées depuis le 1er janvier 1970. L'interface L2 permet de récupérer les mêmes attributs à l'aide des méthodes Performance Timeline, ce qui permet de les placer plus facilement dans une vue chronologique. Elle enregistre ces attributs en utilisant des minuteurs haute résolution.

Avant Navigation Timing, les développeurs essayaient généralement de mesurer les performances de chargement des pages en écrivant du code JavaScript dans l'en-tête du document, comme dans l'exemple de code ci-dessous. Vous pouvez découvrir une démonstration de cette technique sur le site IE Test Drive.

<!DOCTYPE html>

<html>

<head>

<script>

    var start = Date.now();

 

    function sendPageLoad() {

        var now = Date.now();

        var latency = now - start;

        sendAnalytics('Page Load Time: ' + latency);

    }

 

</script>

</head>

<body onload='sendPageLoad()'>

</body>

</html>

Toutefois, cette technique ne mesure pas avec précision les performances de chargement des pages, car elle ne prend pas en compte le temps nécessaire pour obtenir la page auprès du serveur. Par ailleurs, le fait d'exécuter du code JavaScript dans l'en-tête du document est en général une mauvaise habitude en termes de performances.

Resource Timing

Resource Timing fournit des informations de minutage précises relatives à la récupération des ressources de la page. Comme Navigation Timing, Resource Timing fournit des informations de minutage détaillées concernant les différentes phases (redirection, DNS, TCP, demande et réponse) des ressources récupérées. La spécification Resource Timing a été publiée en tant que « Candidate Recommendation » par le W3C. Elle est prise en charge depuis IE10 et Chrome 30.

L'exemple de code ci-dessous utilise la méthode getEntriesByType pour obtenir toutes les ressources initiées par l'élément <img>. Le type d'entrée des ressources est « resource » et le nom correspond à l'URL résolue de la ressource. Vous pouvez découvrir une démonstration de Resource Timing sur le site IE Test Drive.

<!DOCTYPE html>

<html>

  <head>

  </head>

  <body onload='sendResourceTiming()'>

    <img src='https://some-server/image1.png'>

    <img src='https://some-server/image2.png'>

 

    <script>

        function sendResourceTiming()

        {

            var resourceList = window.performance.getEntriesByType('resource');

            for (i = 0; i < resourceList.length; i++)

            {

                if (resourceList[i].initiatorType == 'img')

                {

                    sendAnalytics('Image Fetch Time: ' + resourceList[i].duration);

                }

            }

        }

    </script>

  </body>

</html>

Pour des raisons de sécurité, les ressources cross-origin indiquent uniquement leur heure de départ et leur durée. Les attributs de minutage détaillés ont une valeur nulle. Cette approche permet d'éviter les problèmes de création d'empreintes numériques statistiques : une personne malintentionnée pourrait essayer de déterminer votre appartenance à une organisation en vérifiant si une ressource se trouve ou non dans votre cache, en examinant le temps réseau détaillé. Le serveur cross-origin peut envoyer l'en-tête HTTP timing-allow-origin s'il souhaite partager avec vous les données de minutage.

User Timing

User Timing fournit des informations de minutage détaillées concernant l'exécution des scripts de votre application. Cette interface complète Navigation Timing et Resource Timing, qui fournissent des informations détaillées sur le minutage réseau. User Timing vous permet d'afficher vos informations de minutage de script dans la même vue chronologique que vos données de minutage réseau, pour bénéficier d'un point de vue exhaustif sur les performances de votre application. La spécification User Timing a été publiée en tant que recommandation W3C. Elle est prise en charge depuis IE10 et Chrome 30.

L'interface User Timing définit deux métriques permettant de mesurer le minutage des scripts : marks et measures. La valeur mark représente un timestamp haute résolution situé à un instant bien précis de l'exécution du script. La valeur measure représente la différence entre deux valeurs mark.

Les méthodes suivantes permettent de créer des métriques marks et measures :

void mark(DOMString markName);

void measure(DOMString measureName, optional DOMString startMark, optional DOMString endMark);

Une fois que vous avez ajouté des métriques marks et measures à votre script, vous pouvez récupérer les données de minutage en utilisant les méthodes getEntry, getEntryByType ou getEntryByName. Le type d'entrée des métriques mark est « mark » et le type d'entrée des métriques measure est « measure ».

L'exemple de code ci-dessous utilise les méthodes mark et measure pour mesurer la durée de l'exécution des méthodes doTask1() et doTask2(). Vous pouvez découvrir une démonstration de User Timing sur le site IE Test Drive.

<!DOCTYPE html>

<html>

  <head>

  </head>

  <body onload='doWork()'>

    <script>

        function doWork()

        {

            performance.mark('markStartTask1');

            doTask1();

            performance.mark('markEndTask1');

           

            performance.mark('markStartTask2');

            doTask2();

            performance.mark('markEndTask2');

 

            performance.measure('measureTask1', 'markStartTask1', 'markEndTask1');

            performance.measure('measureTask2', 'markStartTask2', 'markEndTask2');

 

            sendUserTiming(performance.getEntries());

        }

    </script>

  </body>

</html>

Merci à toutes les personnes du groupe de travail Web Performance du W3C pour avoir contribué à la réalisation de ces interfaces, ainsi qu'aux éditeurs de navigateurs pour avoir travaillé sur l'implémentation de cette interface en vue de progresser sur la voie de l'interopérabilité. Grâce à ces interfaces, les développeurs Web peuvent véritablement commencer à mesurer les performances de leurs applications et mieux comprendre comment améliorer ces performances.

Je vous invite à tester les interfaces de mesure des performances avec vos applications dans IE11. Comme toujours, n'hésitez pas à nous faire part de vos commentaires via Connect.

Merci !

Jatinder Mann, Chef de projet Internet Explorer