PHP : comment configurer, utiliser et surveiller OPcache ?

Actualité ekino -

Description des différentes valeurs de configuration PHP à appliquer pour OPcache, explications sur son utilisation et comment surveiller ses performances.

Article paru le :
Article paru le :

OPcache (qui signifie Optimizer Plus Cache) existe depuis maintenant deux ans et il est introduit depuis PHP 5.5.0 dans PHP. Il sert à cacher l’opcode de PHP, c’est-à-dire les instructions de bas niveau générées par la machine virtuelle PHP lors de l’exécution d’un script. De plus, OPcache va optimiser l’exécution du code afin d’en améliorer les performances.

Le code de Zend Optimizer Plus est disponible sur Github et nous nous en servirons pour illustrer quelques exemples afin d’aider à comprendre le fonctionnement des divers paramètres de configuration.

 

Avant de commencer, voici un schéma tiré de la documentation Zend qui vous permettra de comprendre simplement le fonctionnement d’OPcache :

Pour activer l’extension, décommentez et passez le paramètre opcache.enable de votre configuration PHP à 1. 

Passons maintenant à la revue de tous les paramètres de configuration d’OPcache.

 

Configuration

Nous allons maintenant détailler chacun des paramètres de configuration d’OPcache afin de vous aider à choisir au mieux la configuration que vous devez adopter pour vos projets.

 

opcache.memory_consumption

Ce paramètre spécifie la taille de la mémoire partagée OPcache. Vous pouvez utiliser la fonction PHP opcache_get_status() pour avoir un aperçu de ce que OPcache utilise pour cacher l’opcode de votre application.

Par défaut, sa valeur est de 64 Mo.

 

opcache.interned_strings_buffer

La configuration par défaut de ce paramètre est de 4 Mo. Ce qui signifie que OPcache va avoir à sa disposition 4 Mo pour stocker les chaînes de caractères réutilisées. La valeur par défaut peut, pour certaines applications, être insuffisante, n’hésitez pas à l’augmenter si besoin.

En effet, ce paramètre spécifie la taille allouée pour le stockage des chaînes de caractère utilisées dans le code.

 

Par exemple, si vous avez 3 000 fois la chaîne “ekino” dans votre code (nom de classe, méthode, fonction, variable, valeur, propriété), PHP va alors stocker cette valeur une fois et utiliser un pointeur pour les 2 999 autres fois. Ce qui peut être un gain significatif sur les grosses applications.

Ceci devient encore plus intéressant lorsque l’on apprend que ces pointeurs seront bien sûr utilisés par tous les pools php-fpm dont vous disposez.

 

opcache.enable_cli=1

Ce paramètre est désactivé (0) par défaut. Il permet d’activer ou désactiver OPcache pour PHP en mode CLI (Command-Line Interface).

 

opcache.max_accelerated_files

Par défaut, la valeur de ce paramètre est de 2000. Sa valeur doit être comprise entre 200 et 100 000 et sera toujours un nombre premier. Le mieux est donc de mettre directement un nombre premier supérieur afin d’éviter à PHP de faire le traitement.

Ce nombre correspond au nombre de fichiers qui peuvent être cachés par OPcache. Il est donc important d’avoir une valeur supérieure au nombre de fichiers présents dans votre projet car, sinon, OPcache serait arrêté en erreur. Dans ce cas, vous auriez également le message “Insufficient shared memory!” dans vos logs d’erreur.

 

Pour information, un site vierge sous Symfony comptabilise environ 5 300 fichiers PHP.

Vous pouvez vérifier le nombre de fichiers disponibles dans votre projet en lançant simplement la commande suivante :

 

opcache.max_wasted_percentage

Ce paramètre sert à définir le pourcentage de mémoire qui peut être utilisé avant de provoquer un restart du cache d’OPcode, ce qui a pour conséquence de vider tout le cache d’OPcache (chaînes internes, opcode, chemins des fichiers, …). Par défaut, sa valeur est définie à 5%.

Il s’agit donc de la mémoire utilisée pour cacher l’opcode et non celle utilisée pour stocker les interned strings.

 

opcache.use_cwd

Par défaut, la valeur de ce paramètre est à 1, ce qui signifie que les noms des répertoires seront également utilisés (en plus du nom du fichier) pour la génération des clés de cache.

 

Imaginons un script file.php puis un autre dans test/file.php :

La désactivation de ce paramètre augmente les performances mais peut cependant créer des conflits dans vos clés de cache (et donc casser votre application), dans le cas ou vous auriez des fichiers du même nom, ce qui arrive souvent depuis l’arrivée des namespaces PHP.

 

Cela peut, par exemple, être le cas si vous utilisez le framework Symfony dont deux composants (BrowserKit et HttpKernel) disposent d’un fichier nommé Client.php : 

  • vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Client.php
  • vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Client.php

 

opcache.validate_timestamps et opcache.revalidate_freq

Ce paramètre est à 1 par défaut, ce qui signifie que OPcache va rafraîchir le cache des fichiers mis à jour toutes les 2 secondes (valeur par défaut du paramètre opcache.revalidate_freq qui définit la fréquence de mise à jour en secondes).

 

Il ne faut donc passer le paramètre opcache.validate_timestamps à 0 que si vos fichiers PHP ne bougent jamais (ce qui doit être le cas en production).

Pensez également à redémarrer vos php-fpm ou éventuellement appeler la fonction opcache_reset() via un script disponible en HTTP (si vous disposez de plusieurs frontaux, il vous faudra les rendre accessibles) lors des déploiements de votre code afin de bénéficier des mises à jour du code.

 

opcache.revalidate_path

Ce paramètre est également très intéressant. Il est désactivé (0) par défaut, ce qui signifie que les chemins utilisés lors de vos inclusions seront cachés la première fois et une référence vers ceux-ci sera utilisée pour les autres inclusions.

Cela peut être dangeureux dans le cas où vous effectuez une inclusion d’un fichier portant le même nom qu’un autre.

Prennons l’arborescence suivante :

 

avec dans les deux fichiers index.php, l’inclusion suivante :

 

Dans le cas où le premier index.php, à la racine, serait mis en cache, alors le deuxième fichier index.php aurait exactement le même résultat que le premier.

Ceci arrive car une référence serait faite sur ce fichier, même si le fichier included.php n’existe pas dans le répertoire other.

En revanche, si ce paramètre est passé à 1, les chemins sont re-validés à chaque inclusion. Il faut donc bien vérifier son code avant d’activer ce paramètre.

Attention également à la structure utilisée par Capistrano lors des déploiements car celui-ci pointe sur un lien symbolique nommé “current”. Pensez donc bien à réinitialiser votre cache ou laisser ce paramètre actif.

 

opcache.save_comments / opcache.load_comments

Ce paramètre est activé par défaut (1), mais il est possible de le désactiver si vous souhaitez optimiser l’espace nécessaire pour stocker le code optimisé.

Dans le cas ou vous désactiveriez ce paramètre, les commentaires ne seraient alors plus stockés dans le cache d’OPcode (donc plus ressortis lors du chargement depuis le cache).

 

Attention avec ce paramètre si vous utilisez des frameworks ou librairies utilisant des annotations (tels que Symfony, Doctrine, …), par exemple :

Dans ce cas, les annotations ne seraient plus interprétées et l’application serait alors détériorée.

 

Le paramètre opcache.load_comments est également activé par défaut et permet, dans le cas où opcache.save_comments est activé, de remonter les commentaires. Il est bien sûr possible de désactiver cette option et il n’est pas utile de la laisser activée si opcache.save_comments est désactivé.

 

opcache.fast_shutdown

Ce paramètre, désactivé par défaut (0) à cause de problèmes rencontrés sur certains environnements, va permettre d’accélérer la libération de la mémoire en utilisant le moteur de gestion de mémoire de Zend. Les destructeurs seront donc traités plus vite et les requêtes suivantes pourront être enchaînées plus rapidement.

 

opcache.enable_file_override

Ce paramètre est désactivé par défaut (0) et permettra, lorsque vous l’activerez, d’aller chercher directement dans OPcache si le fichier existe lors de l’appel aux fonctions PHP file_exists(), is_file() ou is_readable().

Le fonctionnement de ce paramètre est assez clair lorsque nous allons lire le code de Zend Optimizer Plus.

Ici, par exemple, pour la fonction “file_exists()”, nous récupérons la valeur du cache s’il existe. Sinon, la fonction classique est appelée :

 

Attention, toutefois, si vous désactivez le paramètre opcache.validate_timestamps, les réponses retournées par ces fonctions pourraient être erronées.

 

opcache.optimization_level

Ce paramètre est activé par défaut et défini au niveau le plus haut (0xffffffff), ce qui signifie que plusieurs types d’optimisations vont être effectués afin d’optimiser les opérandes utilisés dans le code. Parmi celles-ci :

Il est bien sûr possible de modifier la valeur de ce paramètre afin de ne pas effectuer certaines optimisations listées ci-dessus.

 

opcache.dups_fix

Ce paramètre (par défaut à 0, désactivé) signifie que lorsqu’une classe est déclarée deux fois dans le même namespace, une erreur sera levée (“Cannot redeclare class…”).

Dans le cas où ce paramètre serait activé, l’erreur ne serait plus remontée. Il semblerait que ceci ne soit qu’un hack disponible. Attention, ce paramètre va également altérer les performances d’exécution du code, il vaut donc mieux le laisser désactivé.

 

Pour illustrer le fonctionnement de ce paramètre, voici le code de Zend Optimizer, on voit ici que l’erreur est retournée uniquement si la variable “ignore_dups” vaut “false”.

 

opcache.blacklist_filename

Ce paramètre vous permet de définir un chemin vers un fichier de “blacklist”. Ce fichier est semblable au .gitigore de Git et vous permettra donc de ne pas activer les optimisations d’OPcache sur les fichiers listés dedans.

Voici un exemple de fichier :

 

opcache.max_file_size

Par défaut à 0, ce paramètre cache tous les fichiers.

Vous pouvez cependant spécifier une taille de fichier maximale des fichiers que vous souhaitez voir dans votre cache. Dans le cas ou le fichier excéderait la taille spécifiée, celui-ci ne sera pas mis en cache.

 

opcache.consistency_checks

Il est par défaut à 0 et va vous permettre de demander à OPcache de vérifier la checksum du fichier (vérifier si le fichier a été modifié) tous les X appels de ce fichier.

Concrètement, voici la vérification faite par le code :

 

Ce paramètre ne devrait être activé qu’en cas de debug car il altère fortement les performances, d’autant plus si sa valeur est petite (le fichier est souvent mis à jour).

 

opcache.force_restart_timeout

Ce paramètre est défini à 180 secondes par défaut et va permettre de forcer la réinitialisation du cache après un temps d’inactivité du cache pendant X secondes.

 

opcache.error_log / opcache.log_verbosity_level

Le paramètre opcache.error_log permet de rediriger les logs d’erreur d’OPcache dans un fichier. Par défaut, ces erreurs sont envoyées sur la sortie d’erreur standard.

 

Le paramètre opcache.log_verbosity_level permet de spécifier la verbosité de ces logs d’erreur. Voici les valeurs que vous pouvez renseigner :

  • 0 : Erreurs fatales,
  • 1 : Erreurs critiques,
  • 2 : Messages de warning,
  • 3 : Messages de debug,
  • 4 : Messages informatifs.

 

opcache.preferred_memory_model

Il s’agit du type de mémoire qui sera utilisé par OPcache pour stocker ses données. Par défaut, OPcache va sélectionner, en fonction de votre système d’exploitation, la bonne valeur à appliquer.

Vous pouvez toutefois forcer cette valeur parmi les suivantes :

  • mmap
  • shm
  • posix
  • win32

 

opcache.protect_memory

Ce paramètre est désactivé par défaut afin d’autoriser l’écriture dans la mémoire partagée à tout moment.

Cependant, pour des raisons de debug, il est possible de l’activer afin de ne pas autoriser OPcache à écrire à nouveau dans sa mémoire partagée.

Concrètement, on note des appels à SHM_UNPROTECT(); puis SHM_PROTECT(); qui entourent toutes les actions d’écriture dans la mémoire partagée dans le code, comme dans l’exemple suivant :

Ces fonctions SHM_UNPROTECT(); et SHM_PROTECT(); vérifient en effet la valeur du paramètre opcache.protect_memory et ne permettent pas l’écriture dans le cas où celui-ci est à 1.

 

opcache.restrict-api

Ce paramètre permet de restreindre l’utilisation des fonctions PHP suivantes :

  • opcache_get_status()
  • opcache_get_configuration()
  • opcache_reset()
  • opcache_invalidate()

 

Par défaut, ce paramètre est vide et il n’y a donc pas de restriction mais il est en effet bon de protéger l’accès à un certain chemin de scripts spécifiques afin de ne pas permettre à n’importe quel script de pouvoir réinitialiser à tout moment le cache de votre application.

Si vous souhaitez restreindre l’utilisation de ces fonctions à un script “opcache.php”, il vous faudra alors spécifier son chemin absolu dans la valeur de ce paramètre.

 

Les fonctions utiles d’OPcache

opcache_get_configuration()

Cette fonction vous permet d’obtenir la configuration PHP d’OPcache, ce qui peut être utile si vous souhaitez construire un dashboard personnalisé pour surveiller votre OPcache. Elle retourne également la version de Zend OPcache utilisée dans votre version de PHP ainsi que la liste des fichiers ignorés (blacklist).

 

opcache_get_status()

Cette fonction vous retourne toutes les informations nécessaires sur l’utilisation de votre OPcache tel que le nombre de clés actuellement en cache, la mémoire utilisée, etc …

 

opcache_compile_file(filename)

Disponible depuis PHP 5.5.5 seulement, cette fonction vous permettra de pré-cacher vos scripts PHP. Elle va en effet compiler le code PHP sans l’exécuter.

 

opcache_invalidate(filename[, force])

Inversement, cette fonction vous permettra d’invalider un script de votre OPcache. Attention toutefois à bien spécifier le second paramètre pour forcer l’invalidation du script. Sinon, le cache du script sera invalidé uniquement si la date de modification est supérieure à la date de mise en cache.

 

opcache_reset()

Cette fonction invalidera tout votre cache d’opcode.

 

Quelques dashboards pour monitorer votre OPcache

Il existe quelques tableaux de bord pour superviser la taille de la mémoire partagée, le nombre de clés en cache, les scripts cachés ainsi que la mémoire utilisée par votre OPcache grâce à quelques tableaux de bord disponibles en open-source.

opcache_dashboard

Voici une liste de quelques-uns d’entre eux :

 

Conclusion

OPcache améliore donc grandement les performances de vos applications PHP.

Il convient par contre de trouver un bon compromis entre votre code applicatif et les paramètres de configuration et d’optimisation liés à OPcache afin de tirer le meilleur parti du cache d’OPcode sans détériorer votre application.

Il est également important de bien superviser votre OPcache afin de garantir que la configuration choisie est adaptée à l’infrastructure système dont vous disposez. Pour cela, plusieurs outils open-source sont à votre disposition.

Pour plus d’informations sur la façon de configurer vos différents environnements applicatifs, je vous invite à regarder cette conférence Comprendre et optimiser OPcache, par Frederic Bouchery et Xavier Leune.

Si vous souhaitez connaître plus en détail le fonctionnement technique d’OPcache, je vous invite également à lire l’article récent de Julien Pauli, sur son blog.

 

OPcache offre la possibilité d’optimiser l’exécution de votre code applicatif mais vous pouvez également mettre en place d’autres stratégies de cache afin d’augmenter davantage les performances et l’expérience utilisateur de votre application. Vous pourrez ainsi imaginer cacher des résultats de requêtes dans des bases de données clé-valeur tel que Redis ou encore implémenter du cache HTTP avec des outils tel que Varnish.

Crédits photo : @OliBac

Laisser un commentaire