Mesurer le Temps d'Exécution d'un Script JS
Mesurer le temps d'exécution d'un script peut s'avérer utile pour optimiser les performances d'une application en JS.
Dans cet article, nous abordons la mesure du temps d’exécution des scripts JavaScript. Pour cela, nous illustrerons le cas concret de l’utilisation des méthodes filter()
et splice()
pour extraire un élément d’un tableau.
Contexte
Lors d’une intervention en école de dev ce matin, j’écris un script visant à supprimer d’un tableau d’objets JS l’élément ayant pour id
1.
J’écris donc quelque chose, comme ça :
const users = [
{ id: 1, pseudo: "Toto" },
{ id: 2, pseudo: "Tutu" },
{ id: 3, pseudo: "Titi" }
];
const usersAfterDelete = users.filter((user) => user.id !== 1);
Le tri effectué par la méthode filter()
permet de générer un nouveau tableau en se basant sur la fonction de filtre indiquée, à savoir : « garde tous les éléments qui ont un id
différent de 1 ».
L’un de mes étudiants (Yohan, c’est ton moment ✨) intervient et me demande pourquoi ne pas avoir privilégié une approche visant simplement à extraire du tableau existant l’élément en question, sans recopie.
Je lui réponds que nous aurions effectivement très bien pu utiliser d’autres méthodes de manipulation de tableaux comme par exemple splice()
, supprimant du tableau existant un élément situé à l’indice précisé. Cela implique néanmoins d’identifier préalablement l’indice de l’élément à supprimer avec la méthode findIndex()
.
Considérant le contexte, je lui suggère un écart relativement minime en termes de performances - et la formation reprend son cours.
Mais cette suggestion ne me suffit pas vraiment. Ça trotte dans ma tête, il faut que je sache… au risque de ne pas dormir cette nuit ! 🫣
Je décide alors d’investiguer plus en profondeur et en profite pour vous partager une petite astuce pour connaître le temps d’exécution d’un script JS.
Connaître le temps d’exécution d’un script
En JavaScript, la méthode performance.now()
renvoie le nombre de millisecondes écoulées depuis le chargement de la page.
Avec une précision allant jusqu’au millionième de seconde, l’utiliser permet de mesurer précisément le temps écoulé entre deux « points » dans notre code source.
performance.now()
s’avère ainsi très utile pour évaluer les performances et le temps d’exécution de différentes opérations.
// Enregistrer le temps de départ
const startTime = performance.now();
// Effectuer une opération quelconque
for (let i = 1; i <= 1000; i++) {
const result = i * i;
}
// Enregistrer le temps d'arrivée
const endTime = performance.now();
// Calculer le temps écoulé en millisecondes
const elapsedTime = endTime - startTime;
console.log(`Le temps écoulé est de ${elapsedTime} ms`);
Dans cet exemple, nous enregistrons le temps avant et après une boucle qui effectue une opération fictive.
En soustrayant le temps de départ du temps d’arrivée, nous obtenons le temps total écoulé pour exécuter cette boucle.
Comparer le temps d’exécution des scripts
Exploitons cette mécanique pour comparer les performances de filter()
et splice()
dans le contexte précédent.
« Fonction chronomètre » sur mesure
Pour éviter d’inonder notre code de performance.now()
, il serait judicieux de créer une fonction executionTime()
, dont le rôle va être de calculer le temps d’exécution d’un script, transmis en argument à la fonction.
function executionTime(cb) {
const start = performance.now();
cb();
const end = performance.now();
return end - start;
}
J’ai nommé le paramètre de cette fonction cb
afin d’expliciter le fait que le script en question sera déclenché en tant que fonction de callback.
Vous pourrez constater que le temps d’exécution mesuré n’est pas toujours le même pour une même opération.
Ces variations sont causées par :
- Les ressources matérielles (CPU, RAM…) allouées aux autres processus en cours d’exécution sur l’appareil
- La charge du système (nombre de traitements parallèles)
- À l’optimisation des navigateurs et des environnements d’exécution
Dataset
Pour comparer les performances de .filter()
et .splice()
dans notre contexte, créons d’abord un dataset. Il s’agit pour l’exemple d’un tableau de x
utilisateurs, définis par un id
et un pseudo
, comme évoqué plus haut.
const sampleSize = 100;
const users = Array.from({ length: sampleSize }, (_, index) => ({
id: index + 1,
pseudo: `User ${index + 1}`,
}));
On obtient ainsi :
const users = [
{ id: 1, pseudo: "User 1" },
{ id: 2, pseudo: "User 2" },
.
.
.
{ id: 100, pseudo: "User 100" }
];
Mesure du temps d’exécution
Avec filter()
filter()
crée un nouveau tableau contenant les éléments qui satisfont une condition spécifiée par une fonction de rappel.
const filterDuration = executionTime(() => {
const newUsers = users.filter((user) => user.id !== sampleSize/2)
});
console.log(`Temps d'exécution de filter() : ${filterDuration} ms`);
Pour 100 utilisateurs
> Temps d'exécution de filter() : 0.03439995574951172 ms
Pour 100 000 utilisateurs
> Temps d'exécution de filter() : 2.343500018119812 ms
Avec splice()
findIndex()
retourne l’index du premier élément dans un tableau qui satisfait une condition spécifiée par une fonction de rappel.
splice()
modifie le contenu d’un tableau en ajoutant ou en supprimant des éléments à des positions spécifiques.
const spliceDuration = executionTime(() => {
const indexToRemove = users.findIndex((user) => user.id === sampleSize/2);
if (indexToRemove !== -1) users.splice(indexToRemove, 1);
});
console.log(`Temps d'exécution de splice() : ${spliceDuration} ms`);
Pour 100 utilisateurs
> Temps d'exécution de splice() : 0.06549996137619019 ms
Pour 100 000 utilisateurs
> Temps d'exécution de splice() : 1.005799949169159 ms
Ce petit script me permet d’arriver à la conclusion suivante :
Méthode | Petit jeu de données | Grand jeu de données |
---|---|---|
filter() | Plus performant | Moins performant |
splice() | Moins performant | Plus performant |
En somme, mesurer le temps d’exécution des scripts JavaScript est crucial pour optimiser les performances des applications. En mesurant le temps d’exécution de certaines opérations, nous pouvons choisir la méthodologie de résolution et les fonctions les plus adaptées au contexte pour obtenir une application la plus performante possible.