Skip to content

Commit

Permalink
docs: add performance docs (#1004)
Browse files Browse the repository at this point in the history
* docs: add performance docs

* docs: add PHP performance section

* Update docs/performance.md

Co-authored-by: Jacob Dreesen <jacob@hdreesen.de>

* Update docs/performance.md

Co-authored-by: Jacob Dreesen <jacob@hdreesen.de>

* Update docs/performance.md

Co-authored-by: Jacob Dreesen <jacob@hdreesen.de>

* Update docs/performance.md

Co-authored-by: Jacob Dreesen <jacob@hdreesen.de>

* Update docs/performance.md

Co-authored-by: Jacob Dreesen <jacob@hdreesen.de>

* Update docs/performance.md

Co-authored-by: Jacob Dreesen <jacob@hdreesen.de>

* Update docs/performance.md

Co-authored-by: Jacob Dreesen <jacob@hdreesen.de>

* typo

* musl

* musl fixes

* add log section

* french translation

* typo

---------

Co-authored-by: Jacob Dreesen <jacob@hdreesen.de>
  • Loading branch information
dunglas and jdreesen committed Aug 27, 2024
1 parent a16076e commit 47257ec
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 2 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ You can also run command-line scripts with:
* [Real-time](https://frankenphp.dev/docs/mercure/)
* [Configuration](https://frankenphp.dev/docs/config/)
* [Docker images](https://frankenphp.dev/docs/docker/)
* [Deploy in production](docs/production.md)
* [Deploy in production](https://frankenphp.dev/docs/production/)
* [Performance optimization](https://frankenphp.dev/docs/performance/)
* [Create **standalone**, self-executable PHP apps](https://frankenphp.dev/docs/embed/)
* [Create static binaries](https://frankenphp.dev/docs/static/)
* [Compile from sources](https://frankenphp.dev/docs/compile/)
Expand Down
1 change: 1 addition & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ php_server [<matcher>] {
split_path <delim...> # Sets the substrings for splitting the URI into two parts. The first matching substring will be used to split the "path info" from the path. The first piece is suffixed with the matching substring and will be assumed as the actual resource (CGI script) name. The second piece will be set to PATH_INFO for the script to use. Default: `.php`
resolve_root_symlink false # Disables resolving the `root` directory to its actual value by evaluating a symbolic link, if one exists (enabled by default).
env <key> <value> # Sets an extra environment variable to the given value. Can be specified more than once for multiple environment variables.
file_server off # Disables the built-in file_server directive.
}
```

Expand Down
1 change: 1 addition & 0 deletions docs/fr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ Vous pouvez également exécuter des scripts en ligne de commande avec :
* [Configuration](config.md)
* [Images Docker](docker.md)
* [Déploiement en production](production.md)
* [Optimisation des performances](performance.md)
* [Créer des applications PHP **standalone**, auto-exécutables](embed.md)
* [Créer un build statique](static.md)
* [Compiler depuis les sources](compile.md)
Expand Down
110 changes: 110 additions & 0 deletions docs/fr/performance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Performance

Par défaut, FrankenPHP essaie d'offrir un bon compromis entre performance et facilité d'utilisation.
Cependant, il est possible d'améliorer considérablement les performances en utilisant une configuration appropriée.

## Nombre de threads et de workers

Par défaut, FrankenPHP démarre 2 fois plus de threads et de workers (en mode worker) que le nombre de CPU disponibles.

Les valeurs appropriées dépendent fortement de la manière dont votre application est écrite, de ce qu'elle fait et de votre matériel.
Nous recommandons fortement de modifier ces valeurs.

Pour trouver les bonnes valeurs, il est souhaitable d'effectuer des tests de charge simulant le trafic réel.
[k6](https://k6.io) et [Gatling](https://gatling.io) sont de bons outils pour cela.

Pour configurer le nombre de threads, utilisez l'option `num_threads` des directives `php_server` et `php`.
Pour changer le nombre de travailleurs, utilisez l'option `num` de la section `worker` de la directive `frankenphp`.

## Mode worker

Activer [le mode worker](worker.md) améliorere considérablement les performances,
mais votre application doit être adaptée pour être compatible avec ce mode :
vous devez créer un script worker et vous assurer que l'application n'a pas de fuite de mémoire.

## Ne pas utiliser musl

Les binaires statiques que nous fournissons et la variante Alpine Linux des images Docker officielles
utilisent [la librairie musl](https://musl.libc.org).

PHP est connu pour être significativement plus lent lorsqu'il utilise cette bibliothèque C alternative au lieu de la bibliothèque GNU traditionnelle,
surtout lorsqu'il est compilé en mode ZTS (thread-safe), ce qui est nécessaire pour FrankenPHP.

En outre, certains bogues ne se produisent que lors de l'utilisation de musl.

Dans les environnements de production, nous recommandons fortement d'utiliser la glibc.

Ceci peut être réalisé en utilisant les images Docker Debian (par défaut) et [en compilant FrankenPHP à partir des sources](compile.md).

Alternativement, nous fournissons des binaires statiques compilés avec [l'allocateur mimalloc](https://github.com/microsoft/mimalloc), ce qui rend FrankenPHP+musl plus rapide (mais toujours plus lent que FrankenPHP+glibc).

## Configuration du runtime Go

FrankenPHP est écrit en Go.

En général, le runtime Go ne nécessite pas de configuration particulière, mais dans certaines circonstances,
une configuration spécifique améliore les performances.

Vous voudrez probablement mettre la variable d'environnement `GODEBUG` à `cgocheck=0` (la valeur par défaut dans les images Docker de FrankenPHP).

Si vous exécutez FrankenPHP dans des conteneurs (Docker, Kubernetes, LXC...) et que vous limitez la mémoire disponible pour les conteneurs,
mettez la variable d'environnement `GOMEMLIMIT` à la quantité de mémoire disponible.

Pour plus de détails, [la page de documentation Go dédiée à ce sujet](https://pkg.go.dev/runtime#hdr-Environment_Variables) est à lire absolument pour tirer le meilleur parti du runtime.

## `file_server`

Par défaut, la directive `php_server` met automatiquement en place un serveur de fichiers pour
pour servir les fichiers statiques (assets) stockés dans le répertoire racine.

Cette fonctionnalité est pratique, mais a un coût.
Pour la désactiver, utilisez la configuration suivante :

```caddyfile
php_server {
file_server off
}
```

## *Placeholders*

Vous pouvez utiliser des [*placeholders*](https://caddyserver.com/docs/conventions#placeholders) dans les directives `root` et `env`.
Cependant, cela empêche la mise en cache de ces valeurs et a un coût important en termes de performances.

Si possible, évitez les *placeholders* dans ces directives.

## `resolve_root_symlink`

Par défaut, si le *document root* est un lien symbolique, il est automatiquement résolu par FrankenPHP (c'est nécessaire pour le bon fonctionnement de PHP).
Si la racine du document n'est pas un lien symbolique, vous pouvez désactiver cette fonctionnalité.

```caddyfile
php_server {
resolve_root_symlink false
}
```

Cela améliorera les performances si la directive `root` contient des [*placeholders*](https://caddyserver.com/docs/conventions#placeholders).
Le gain sera négligeable dans les autres cas.

## Journaux

La journalisation est évidemment très utile, mais, par définition, elle nécessite des opérations d'*I/O* et des allocations de mémoire,
ce qui réduit considérablement les performances.
Assurez-vous de [définir le niveau de journalisation](https://caddyserver.com/docs/caddyfile/options#log) correctement,
et de ne journaliser que ce qui est nécessaire.

## Performances de PHP

FrankenPHP utilise l'interpréteur PHP officiel.
Toutes les optimisations de performances habituelles liées à PHP s'appliquent à FrankenPHP.

En particulier :

* vérifiez que [OPcache](https://www.php.net/manual/en/book.opcache.php) est installé, activé et correctement configuré
* activez [les optimisations de l'autoloader de Composer](https://getcomposer.org/doc/articles/autoloader-optimization.md)
* assurez-vous que le cache `realpath` est suffisamment grand pour les besoins de votre application
* utilisez le [pré-chargement](https://www.php.net/manual/en/opcache.preloading.php)

Pour plus de détails, lisez [l'entrée de la documentation dédiée de Symfony](https://symfony.com/doc/current/performance.html)
(la plupart des conseils sont utiles même si vous n'utilisez pas Symfony).
110 changes: 110 additions & 0 deletions docs/performance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Performance

By default, FrankenPHP tries to offer a good compromise between performance and ease of use.
However, it is possible to substantially improve performance using an appropriate configuration.

## Number of Threads and Workers

By default, FrankenPHP starts 2 times more threads and workers (in worker mode) than the available numbers of CPU.

The appropriate values depend heavily on how your application is written, what it does and your hardware.
We strongly recommend changing these values.

To find the right values, it's best to run load tests simulating real traffic.
[k6](https://k6.io) and [Gatling](https://gatling.io) are good tools for this.

To configure the number of threads, use the `num_threads` option of the `php_server` and `php` directives.
To change the number of workers, use the `num` option of the `worker` section of the `frankenphp` directive.

## Worker Mode

Enabling [the worker mode](worker.md) dramatically improves performance,
but your app must be adapted to be compatible with this mode:
you need to create a worker script and to be sure that the app is not leaking memory.

## Don't Use musl

The static binaries we provide and the Alpine Linux variant of the official Docker images
are using [the musl libc](https://musl.libc.org).

PHP is known to be significantly slower when using this alternative C library instead of the traditional GNU library,
especially when compiled in ZTS mode (thread-safe), which is required for FrankenPHP.

Also, some bugs also only happen when using musl.

In production environements, we strongly recommend to use the glibc.

This can be achieved by using the Debian Docker images (the default) and [by compiling FrankenPHP from sources](compile.md).

Alternatively, we provide static binaries compiled with [the mimalloc allocator](https://github.com/microsoft/mimalloc), which makes FrankenPHP+musl faster (but still slower than FrankenPHP+glibc).

## Go Runtime Configuration

FrankenPHP is written in Go.

In general, the Go runtime doesn't require any special configuration, but in certain circumstances,
specific configuration improves performance.

You likely want to set the `GODEBUG` environment variable to `cgocheck=0` (the default in the FrankenPHP Docker images).

If you run FrankenPHP in containers (Docker, Kubernetes, LXC...) and limit the memory available for the containers,
set the `GOMEMLIMIT` environment variable to the available amount of memory.

For more details, [the Go documentation page dedicated to this subject](https://pkg.go.dev/runtime#hdr-Environment_Variables) is a must-read to get the most out of the runtime.

## `file_server`

By default, the `php_server` directive automatically sets up a file server to
serve static files (assets) stored in the root directory.

This feature is convenient, but comes with a cost.
To disable it, use the following config:

```caddyfile
php_server {
file_server off
}
```

## Placeholders

You can use [placeholders](https://caddyserver.com/docs/conventions#placeholders) in the `root` and `env` directives.
However, this prevents caching these values, and comes with a significant performance cost.

If possible, avoid placeholders in these directives.

## `resolve_root_symlink`

By default, if the document root is a symbolic link, it is automatically resolved by FrankenPHP (this is necessary for PHP to work properly).
If the document root is not a symlink, you can disable this feature.

```caddyfile
php_server {
resolve_root_symlink false
}
```

This will improve performance if the `root` directive contains [placeholders](https://caddyserver.com/docs/conventions#placeholders).
The gain will be negligible in other cases.

## Logs

Logging is obviously very useful, but, by definition,
it requires I/O operations and memory allocations, which considerably reduces performance.
Make sure you [set the logging level](https://caddyserver.com/docs/caddyfile/options#log) correctly,
and only log what's necessary.

## PHP Performance

FrankenPHP uses the official PHP interpreter.
All usual PHP-related performance optimizations apply with FrankenPHP.

In particular:

* check that [OPcache](https://www.php.net/manual/en/book.opcache.php) is installed, enabled and properly configured
* enable [Composer autoloader optimizations](https://getcomposer.org/doc/articles/autoloader-optimization.md)
* ensure that the `realpath` cache is big enough for the needs of your application
* use [preloading](https://www.php.net/manual/en/opcache.preloading.php)

For more details, read [the dedicated Symfony documentation entry](https://symfony.com/doc/current/performance.html)
(most tips are useful even if you don't use Symfony).
5 changes: 4 additions & 1 deletion testdata/benchmark.Caddyfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ http:// {
root * .

encode zstd br gzip
php_server
php_server {
file_server off
resolve_root_symlink false
}
}
}

0 comments on commit 47257ec

Please sign in to comment.