Le tradeoff entre lisibilité et performance

Il y a quelques années de ça, le plus important dans la création de programme était de créer un programme ayant de bonnes performances.

Par bonnes performances on entend qu’il s’exécute rapidement et qu’il nécessite peu de mémoire RAM pour fonctionner.

Cependant, à partir des années 2000 et le développement d’internet, la nécessité de fournir un code facilement lisible, maintenable et modifiable est devenu de plus en plus nécessaire.

En effet, aujourd’hui les équipes interviennent quotidiennement sur la base de code d’une application web, que ce soit pour corriger de anomalies, pour ajouter de nouvelles fonctionnalités, ou pour réaliser des migrations d’une technologie obsolète à une technologie plus moderne. Par conséquent un code difficilement lisible est un vrai frein dans le travail.

Le problème est alors de définir les éléments qui rendent un code lisible, et de s’accorder sur les règles à suivre. Sinon il y aura des débats sans fins aux seins des différentes équipes.

Il y a des langages qui ont définit des recommandations standards comme le PHP avec PHP-FIG, ou le Python avec le PEP-8.

En JS, malheureusement il y a plusieurs recommandations :

Cependant le formatage n’est pas toujours suffisant, et il peut être nécessaire de se référer à un ensemble de règles. Une référence bien connu est celle des livres de Uncle Bob, mais je n’en parlerais pas car je ne les ait pas lu.

J’ai déjà eu du mal à imposer le respect des recommandations standard en PHP dans certaines expériences professionnelles. Donc imposer la lecture de livres complexes pour la plupart non traduit (donc en anglais) me semble utopique et non raisonnable.

D’autant plus, et c’est le point de cet article qu’il y a un tradeoff entre la lisibilité et la performance.

Donc pour améliorer la lisibilité, personnellement j’ai tendance à me reposer sur les règles d’un code callisthénique.

C’est règles sont au nombre de 9 et les voici :

  1. Un seul niveau d’indentation par méthode
  2. Ne pas utiliser le mot clé ELSE
  3. Envelopper tous les types primitifs
  4. Collections “de première classe”
  5. Un « dot » ou « arrow » par ligne (hors this)
  6. Ne pas utiliser d’abréviation
  7. Garder petites toutes les entités
  8. Aucune classe avec plus de deux variables d’instance
  9. Pas de Getters / Setters / Properties

Pour plus d’informations je vous invite à aller voir ce billet de blog qui explique très bien les règles du code callisthénique.

Il est bien évident qu’il est possible dans de nombreux cas d’améliorer la lisibilité sans pour autant réduire la performance. Cependant dans certains cas, il y aura un choix à faire.

Pour illustrer ce point, je vais me reposer sur la 1ère règle : « Un seul niveau d’indentation par méthode« 

Dans l’exemple que je vais vous montrer il n’y aura qu’un seul niveau d’indentation.
Mais c’est un exemple, et l’important c’est de comprendre l’idée de réduire le nombre de niveau d’indentation.

Pour illustrer cela j’ai réaliser une expérience sur un tableau de 100 000 000 de valeurs, sur lequel on se base pour réaliser un second tableau, qui ne recevra que les nombre pair du premier tableau auxquels seront soustrait 5.

Voici le 1er exemple :

let table1 = [];
for (let i = 0; i < 100000000; i++) {
    table1.push(i);
}

let t1 = performance.now();
const table2 = table1.map(element => {
    if (element % 2 === 0) {
        return element - 5;
    }
});
let t2 = performance.now();

console.log("La structure map-if : " + (t2 - t1) + " millisecondes.");

Et voici le résultat :

La structure map-if : 2627.2699999972247 millisecondes.

Et voici l’exemple avec aucun niveau d’indentation. Il y a des indentations pour aligner filter et map et ainsi améliorer l’esthétique et la lisibilité, mais ça ne compte pas comme des niveaux d’indentation. La définition de table2 pourrait très bien se faire en une seule ligne.

let table1 = [];
for (let i = 0; i < 100000000; i++) {
    table1.push(i);
}

let t1 = performance.now();
const table2 = table1
                .filter(element => element % 2 === 0)
                .map(element => element -5);
let t2 = performance.now()

console.log("La structure filter-map : " + (t2 - t1) + " millisecondes.");

Et voici le résultat :

La structure filter-map : 4860.84000003757 millisecondes.

Cela représente tout de même environ 2 secondes de différence.

Le but n’est pas de dire qu’il ne faut donc pas faire attention à la lisibilité. Je pense au contraire que c’est un point vital !

Mais le but est de mettre en lumière ce tradeoff qu’il peut parfois y avoir entre la lisibilité et la performance.

Pour marque-pages : Permaliens.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *