Tirer le maximum du moteur PHP 7 - l'exemple de Symfony

Tirer le maximum du moteur PHP 7 - l'exemple de Symfony

PHP 7.0 est déjà de l'histoire ancienne. Mais savez-vous en tirer partie à fond ? Si le moteur est plus rapide sur toutes les opérations en général, il y en a quelques unes qui sont particulièrement optimisées. Avec PHP 5, vous avez peut-être pris certains réflexes qui ne sont plus d'actualité, pour soi-disant écrire du code plus performant ? Je vous propose de passer en revues les différentes techniques d'optimisation mises en place dans Symfony, qui font de la v4 la plus rapide jamais publiée. Ce sera l'occasion de tordre le coup à quelques idées reçues, et de vous en donner quelques autres pour le jour où vous chercherez à presser les dernières millisecondes hors de cette boucle intensive. Benchmark à l'appui évidement.

6baa34bc1e5c347b1003f6abe8691de1?s=128

Nicolas Grekas

March 30, 2018
Tweet

Transcript

  1. Getting the most out of the PHP 7 engine -

    the example of
  2. @nicolasgrekas joind.in/23845

  3. • Born in 1995 – v7 end of 2016 •

    870,000 C lines of code • Distributed leadership The PHP engine
  4. blog.jpauli.tech nikic.github.io

  5. Synchronous and parallel Master Child 1 Child 2 Child n

    index.php index.php index.php index.php index.php index.php PHP’s memory manager insulates each scripts
  6. None
  7. spl_autoload_register( Composer\Autoload\ClassLoader) Implement more in userland

  8. Cache

  9. Persistent memory pools Master Child n index.php index.php index.php Process

    memory Shared memory
  10. • realpath_cache_size=1M • clearstatcache() • Compiled RegExps • OPcache What

    does PHP cache?
  11. OPcache?

  12. Steps to execute a script index.php Lexing + parsing AST

    opcodes Compiling Executing result
  13. First steps are immutable index.php Lexing + parsing AST opcodes

    Compiling Executing result Shared memory* *append-only
  14. Compile time optimizations AST opcodes Compiling Shared memory • Compile-time

    evaluation 'foo'."bar" • Dead-code elimination if (false) {…}
  15. Compile time optimizations AST opcodes Compiling Shared memory • Compile-time

    evaluation 'foo'."bar" • Dead-code elimination if (false) {…} • Interned strings "foobar" len=6 hash=Ox1234 • Immutable arrays [123, ["ab" => 34]] len=2 len=1
  16. Compile time optimizations AST opcodes Compiling Shared memory • Compile-time

    evaluation 'foo'."bar" • Dead-code elimination if (false) {…} • Interned strings "foobar" len=6 hash=Ox1234 • Immutable arrays [123, ["ab" => 34]] len=2 len=1
  17. • Works for constants 'cli' === PHP_SAPI • Works for

    some native functions 1 === count([123]) • Needs fully-qualified identifiers namespace App; \count([123]); Compile time evaluation
  18. namespace App; is_array($a); \is_array($a); Compile time inlining https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues/3048 strlen(), is_{type}(),

    {type}val() casts, defined(), chr(), ord(), call_user_function(), call_user_function_array(), get_class(), get_called_class(), gettype(), count(), in_array(), array_slice(), func_num_args(), func_get_args(), function_exists(), is_callable(), extension_loaded(), constant(), dirname()
  19. Compile time inlining https://github.com/symfony/symfony/pull/25854

  20. Runtime-resolved identifiers are cached in the (local) opcode array Don’t

    « \ » all function calls Stick to the short list
  21. Let’s split the container in one file per service PHP

    7 is slow… at compiling https://github.com/symfony/symfony/pull/23678
  22. • Loading opcodes is really fast • Still takes time

    and memory • « require » is also really fast opcodes move from shared memory to process’ https://github.com/symfony/symfony/pull/23678
  23. Interned strings and immutable arrays stay in shared memory

  24. Leveraging shared memory https://blog.blackfire.io/speeding-up-autoloading-on-php-5-6-7-0-for-everyone.html https://blog.blackfire.io/php-7-performance-improvements-immutable-arrays.html

  25. • Shared memory is read-only $a = […]; $a[0] =

    123; • Duplication happens on « write » $a = "abc"; $b = $a . "def"; $c = $b . "ghi"; (by the way, scalars are free, and declared properties make objects very compact) https://blog.blackfire.io/php-7-performance-improvements-ints-floats-free.html ❤ Copy-on-write
  26. • AbstractRecursivePass • ClassLoader • VarCloner $cache[$k][$v] = [$k =>

    $v]; Not triggering copy-on-write
  27. What else can be slow?

  28. Bypassing the autoloader

  29. • gc_collect_cycles() autotriggered past 10k objects • gc_mem_caches() PHP7 memory

    manager is lazy • Much improved in PHP 7.3 PHP garbage collector
  30. • Coalesce operator A ?? B vs isset(A) ? A

    : B https://github.com/symfony/symfony/pull/26161 • Ropes for encapsed strings "$a and $b" vs $a." and ".$b https://blog.blackfire.io/php-7-performance-improvements-encapsed-strings-optimization.html • Packed arrays (incrementing keys only) https://blog.blackfire.io/php-7-performance-improvements-packed-arrays.html • Class constants can be slow https://github.com/symfony/symfony/pull/25474 - https://github.com/twigphp/Twig/pull/2636 More goodies
  31. • Reference mismatches are gone https://blog.blackfire.io/php-7-performance-improvements-references-mismatch.html • Copy-on-write is not

    triggered by (string)/(array) cast • Since PHP 7.2, « switch » statements use a hashmap • JIT is actively worked on for PHP 8 (no ETA) Even more goodies
  32. https://rawgit.com/kocsismate/php-di-container-benchmarks/master/var/benchmark.html

  33. http://www.phpbenchmarks.com/en/comparator/frameworks.html

  34. None
  35. Conclusion • Wow PHP 7 • Source ideas in PHP’s

    C • Use only in tight loops • Never stop measuring
  36. None