Optimisation PHP et passage par référence

Nous sommes amenés dans nos développements, que ce soit pour les différentes visualisations ou le crawl des sites, à manipuler un nombre de données très important. En conséquence nous apportons une vigilance toute particulière à l’optimisation de notre code PHP afin d’obtenir un optimum dans les temps de traitements.

Pour ce faire, le site The PHP Benchmark, me sert de référence sur certaines optimisations à suivre mais tout n’est pas indiqué, il faut quelque fois extrapoler sur les cas rencontrés. Dans cet article c’est un de ces cas que je vais détailler sur l’optimisation apportée.

Le cas de traitement que nous allons optimiser étant le suivant : Boucler sur un ensemble important d’objets (de l’ordre de 100 000) afin de mettre à jour une propriété de chaque objet. Chaque objet étant référencé par une clé.

Dans le cadre de cet article les données sont sous la forme d’un tableau associatif dont la clé est un nombre et la valeur un autre nombre. La mise à jour consistant pour une clé donnée à augmenter la valeur de plus un.

Le jeu de test est construit de la manière suivante :

 
// Données de test
$aHash1 = range(0, 100000);
$aHash2 = range(0, 100000);
shuffle($aHash1);
shuffle($aHash2);
$aHash = array_combine($aHash1, $aHash2);
unset($aHash1,$aHash2);</code>

Pour ce cas de traitement nous avons en première analyse deux manières de procéder.

Cas 1: Utilisation de array_keys & for

 
echo "Memory ".memory_get_usage() . PHP_EOL;
$start = microtime(true);
$key = array_keys($aHash);
$size = sizeOf($key);
for ($i=0; $i<$size; $i++) $aHash[$key[$i]]++; 
$end = microtime(true); 
echo "array_keys & for: ".($end - $start).PHP_EOL; 
echo "Memory ".memory_get_usage() . PHP_EOL; 

Résultat 1:

Memory 8661216
array_keys & for: 0.12000703811646
Memory 17186176

Le temps de traitement parait correcte mais nous avons une utilisation importante de la mémoire du fait de l’utilisation du tableau des clés $key.

Cas 2: Utilisation de foreach

 
echo "Memory ".memory_get_usage() . PHP_EOL; 
$start = microtime(true); 
foreach($aHash as $key=>$val) $aHash[$key]++;
$end = microtime(true);
echo "foreach: ".($end - $start).PHP_EOL;
echo "Memory ".memory_get_usage() . PHP_EOL; 

Résultat 2:

Memory 8660592
Foreach : 0.13900804519653
Memory 8660944

Un temps de traitement un “poil” plus long que le cas1 mais tout de même acceptable. Par contre peu d’utilisation de la mémoire.

Nous pourrions nous satisfaire de cette solution, mais ne serait il pas possible de gagner quelques micros secondes ?

Une piste de réflexion à la section Using the &-ref-operator de The PHP Benchmark. En effet on remarque que le meilleur temps est obtenu avec l’utilisation du signe de référence &. Et dans notre cas de traitement, si on appliquait le passage par référence, quel serait le résultat ?

Cas 3: Utilisation de foreach par référence

 
echo "Memory ".memory_get_usage() . PHP_EOL; 
$start = microtime(true);
foreach($aHash as &$val) $val++;
unset($val);
$end = microtime(true);
echo "foreach by Ref: ".($end - $start).PHP_EOL;
echo "Memory ".memory_get_usage() . PHP_EOL; 

Résultat 3:

Memory 8660592
foreach by Ref: 0.04000186920166
Memory 8660800

Comme vous pouvez le constater sur ce dernier cas, préfacer la valeur de $val par & signifie que la variable est passée par référence et non par valeur.
Et Bingo ! nous avons toujours une faible utilisation de la mémoire avec un temps de traitement amélioré.

Mais attention sur l’utilisation du passage par référence. La variable $val étant une référence, signifie que toute modification de $val entraînera une modification sur laquelle la variable fait référence. Dans notre cas le dernier élément du tableau. Il donc nécessaire de détruire la variable (unset) afin de s’éviter des bugs à se cogner la tête contre un mur.

L’utilisation de variable par référence est très puissante mais cela doit être utilisé correctement et avec prudence. Mais plus encore, il faut être conscient de la réutilisation de variables pour de nouveaux traitements et donc de leur initialisation. Une bonne pratique étant de systématiquement initialiser les variables avant de les utiliser.

Bon certes cela ne révolutionne pas le monde, mais je vous propose de continuer à partager ce type de détails intéressants sur PHP que je trouverais au fur et à mesure de nos développements.

Christian
@LeMoussel

Tweeter « Optimiser son code PHP avec les références »

Partager sur Facebook

Partager sur Google plus

D’autres articles techniques ? Avec plaisir :

One thought on “Optimisation PHP et passage par référence

  • Michael says: (31 mai 2016 à 5 h 44 min)

    Bonjour,
    Merci pour ce partage d’expérience qui risque de m’etre fort utile,
    Calculer la dimension du tableau avant la boucle est déjà une petite chose qui diminue grandement le temps d exécution, elle mérite d être citée :

    $size = sizeOf($key);
    for ($i=0; $i<$size; $i++) $aHash[$key[$i]]++;

    Plutôt que
    for ($i=0; $i< sizeOf($key); $i++) $aHash[$key[$i]]++;
    Et encore bravo pour se merveilleux outil que vous avez développé

Laisser un commentaire

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