PHP OPCache, Realpath Cache and Preloading (PHPBenelux Conference 2020)

PHP OPCache, Realpath Cache and Preloading (PHPBenelux Conference 2020)

Everybody wants quick applications. A lot of that speed can be gained by the way you write your software, but a big chunk has to do with the way PHP is configured. As PHP matured, it got quicker, it used less memory and it accumulated a lot of settings which can be tuned for performance.

The biggest, and often most misunderstood, features for performance are OPCache (introduced in 5.5) and preloading (introduced in 7.4). This talk covers how both features work, how you can take advantage of them on your servers and during deployments, and tries to show all the ini settings relevant for performance.

4ce755c7f3ddf4e0c92e1aeaeea7677b?s=128

Jachim Coudenys

January 24, 2020
Tweet

Transcript

  1. @coudenysj - jachim.be PHP OPCache, Realpath Cache and Preloading How

    to tune your PHP installation
  2. @coudenysj - jachim.be New PHP versions go faster, but we

    can make them go even faster!
  3. @coudenysj - jachim.be Some groundwork

  4. @coudenysj - jachim.be PHP • Scripting Language • Fire and

    forget • No "manual" compilation
  5. @coudenysj - jachim.be PHP-FPM • Only "relevant" SAPI nowadays :)

    • Master process • Dynamic worker child processes • Shared memory in the master jachimbe 24546 0.0 0.0 301644 10152 ? Ss 2019 1:04 php-fpm: master process (/conf/jachimbe/php/fpm.conf) jachimbe 21457 3.6 0.1 304680 35732 ? S 10:57 0:00 \_ php-fpm: pool jachimbe jachimbe 21458 3.3 0.1 304680 35672 ? S 10:57 0:00 \_ php-fpm: pool jachimbe jachimbe 21459 3.3 0.1 304680 35672 ? S 10:57 0:00 \_ php-fpm: pool jachimbe jachimbe 21460 4.0 0.1 304680 35672 ? S 10:57 0:00 \_ php-fpm: pool jachimbe
  6. @coudenysj - jachim.be Shared memory??? In computer hardware, shared memory

    refers to a (typically large) block of random access memory (RAM) that can be accessed by several different central processing units (CPUs) in a multiprocessor computer system.
  7. @coudenysj - jachim.be All set?

  8. @coudenysj - jachim.be Jachim Coudenys

  9. @coudenysj - jachim.be So this talk is about performance, right?

  10. @coudenysj - jachim.be Right!

  11. @coudenysj - jachim.be Realpath Cache

  12. @coudenysj - jachim.be Realpath Cache • Is used to reduce

    IO • Caches all possible paths to dest path • explode('/') • No shared memory! Mem*Workers • 4M ◦ Prior to PHP 7.0.16 and 7.1.2, the default was "16K" • Helps a lot with NFS
  13. @coudenysj - jachim.be php.net/manual/en/ref.filesystem.php • realpath_cache_get() • realpath_cache_size() • realpath()

  14. @coudenysj - jachim.be demo php realpath.php

  15. @coudenysj - jachim.be <?php $presentation = file_get_contents( __DIR__ . '/../ffi.php'

    ); $presentation2 = file_get_contents( dirname(__DIR__) . '/ffi.php' ); $filesize = filesize( __DIR__ . '/../est.txt' ); print_r(realpath_cache_get());
  16. @coudenysj - jachim.be [/home/jachim/demo] => Array ( [key] => 1.6354972010384E+19

    [is_dir] => 1 [realpath] => /home/jachim/demo [expires] => 1579859105 ) [/home] => Array ( [key] => 4353355791257440477 [is_dir] => 1 [realpath] => /home [expires] => 1579859105 ) [/home/jachim] => Array ( [key] => 5522554812971572568 [is_dir] => 1 [realpath] => /home/jachim [expires] => 1579859105 )
  17. @coudenysj - jachim.be [/home/jachim/demo/../ffi.php] => Array ( [key] => 1.6164035761241E+19

    [is_dir] => [realpath] => /home/jachim/ffi.php [expires] => 1579859105 ) [/home/jachim/ffi.php] => Array ( [key] => 5100116734180765326 [is_dir] => [realpath] => /home/jachim/ffi.php [expires] => 1579859105 ) [/home/jachim/demo/realpath.php] => Array ( [key] => 1.8190176096283E+19 [is_dir] => [realpath] => /home/jachim/demo/realpath.php [expires] => 1579859105 )
  18. @coudenysj - jachim.be Realpath Cache: what can we tune?

  19. @coudenysj - jachim.be php.net/manual/en/ini.core.php • realpath_cache_size • realpath_cache_ttl

  20. @coudenysj - jachim.be OPCache OPcache improves PHP performance by storing

    precompiled script bytecode in shared memory, thereby removing the need for PHP to load and parse scripts on each request.
  21. @coudenysj - jachim.be https://engineering.facile.it/blog/eng/realpath-cache-is-it- all-php-opcache-s-fault/

  22. @coudenysj - jachim.be Throwaway language • Always performs same steps

    ◦ Read PHP code ◦ Lexing + Parsing: Tokens ◦ Compiling: Opcodes ◦ Executing • All information is discarded • Request per request
  23. @coudenysj - jachim.be https://engineering.facile.it/blog/eng/realpath-cache-is-it- all-php-opcache-s-fault/

  24. @coudenysj - jachim.be opcodes? In computing, an opcode (abbreviated from

    operation code) is the portion of a machine language instruction that specifies the operation to be performed.
  25. @coudenysj - jachim.be Vulcan Logic Dumper https://3v4l.org/A1B4H/vld

  26. @coudenysj - jachim.be This can get big quite quickly •

    Libraries • Frameworks • etc...
  27. @coudenysj - jachim.be OPCodes (usually) don't change • Let’s add

    in some cache!
  28. @coudenysj - jachim.be OPCache • Bunch of opcode cachers ◦

    APC ◦ Turck MMCache ◦ Zend Optimizer ◦ etc... • Zend Optimizer "donated" by Zend • Since PHP 5.5
  29. @coudenysj - jachim.be https://engineering.facile.it/blog/eng/realpath-cache-is-it- all-php-opcache-s-fault/

  30. @coudenysj - jachim.be Shared memory • Stored in the master

    process of FPM
  31. @coudenysj - jachim.be OPCache Optimizer • Gets better every release

    • Optimizes: ◦ Branches ◦ Dead code ◦ ++$a vs $a++
  32. @coudenysj - jachim.be Optimized opcodes (example) https://3v4l.org/A1B4H/vld

  33. @coudenysj - jachim.be php.net/manual/en/ref.opcache.php • Information ◦ opcache_get_configuration() ◦ opcache_get_status()

    ◦ opcache_is_script_cached() • Actions ◦ opcache_compile_file() ◦ opcache_invalidate() ◦ opcache_reset()
  34. @coudenysj - jachim.be demo php opcache.php

  35. @coudenysj - jachim.be <?php // php -d opcache.enable_cli=On opcache.php print_r(opcache_get_status());

  36. @coudenysj - jachim.be [opcache_enabled] => 1 [cache_full] => [restart_pending] =>

    [restart_in_progress] => [memory_usage] => Array ( [used_memory] => 9168648 [free_memory] => 125049080 [wasted_memory] => 0 [current_wasted_percentage] => 0 ) [interned_strings_usage] => Array ( [buffer_size] => 6291008 [used_memory] => 371880 [free_memory] => 5919128 [number_of_strings] => 7869 ) ...
  37. @coudenysj - jachim.be [opcache_statistics] => Array ( [num_cached_scripts] => 1

    [num_cached_keys] => 2 [max_cached_keys] => 16229 [hits] => 0 [start_time] => 1579859174 [last_restart_time] => 0 [oom_restarts] => 0 [hash_restarts] => 0 [manual_restarts] => 0 [misses] => 1 [blacklist_misses] => 0 [blacklist_miss_ratio] => 0 [opcache_hit_rate] => 0 ) ...
  38. @coudenysj - jachim.be [scripts] => Array ( [/home/jachim/demo/opcache.php] => Array

    ( [full_path] => /home/jachim/demo/opcache.php [hits] => 0 [memory_consumption] => 880 [last_used] => Fri Jan 24 10:46:14 2020 [last_used_timestamp] => 1579859174 [timestamp] => 1579858691 ) ) ...
  39. @coudenysj - jachim.be Interned strings • Used in a lot

    of languages • Since PHP 5.4 • "Compression" for source code • In shared memory (FPM master process)
  40. @coudenysj - jachim.be Keys • Full path to file •

    Relative paths to files
  41. @coudenysj - jachim.be Wasted memory • Opcache doesn't do "defragmentation"

    • File changes cause recompilation
  42. @coudenysj - jachim.be Opcache restarts • OOM Restarts ◦ Memory

    • Hash Restarts ◦ Keys • Manual Restarts ◦ opcache_reset()
  43. @coudenysj - jachim.be NEVER have a full cache!

  44. @coudenysj - jachim.be APCu • User Shared memory • apc_*

    compatible
  45. @coudenysj - jachim.be Opcache File Cache • Read opcodes from

    disk • Can help busy sites • Supports CLI (used to be "fire and forget") • Cache to "user directory" opcache.file_cache=/var/tmp/php/opcache opcache.file_cache_only=1 # Useful for CLI opcache.file_cache_consistency_checks=1 # Adler checksum
  46. @coudenysj - jachim.be demo php -d opcache.enable_cli=On -d opcache.file_cache="/tmp/opcache" opcache.php

  47. @coudenysj - jachim.be <?php // php -d opcache.enable_cli=On -d opcache.file_cache="/home/jachim/demo/cache"

    opcache.php print_r(opcache_get_status());
  48. @coudenysj - jachim.be └╼ tree cache cache └── 26f7903bdd40555497c24242ea455a66 └──

    home └── jachim └── demo └── opcache.php.bin 4 directories, 1 file
  49. @coudenysj - jachim.be PHPArch Opcache article https://www.phparch.com/article/diving-in-the -opcache/ • Explores

    the option of using opcache file caching as pre-compiled PHP programs.
  50. @coudenysj - jachim.be OPCache: what can we tune?

  51. @coudenysj - jachim.be “Disaster Recovery” Scenarios • Memory Full? •

    Interned Strings Full? • Key store full?
  52. @coudenysj - jachim.be php.net/manual/en/opcache.configuration.php • Memory ◦ opcache.memory_consumption ◦ opcache.interned_strings_buffer

    ◦ opcache.max_accelerated_files (keys) • Invalidation ◦ opcache.validate_timestamps ◦ opcache.revalidate_freq • Thresholds ◦ opcache.max_wasted_percentage
  53. @coudenysj - jachim.be Getting stats • opcache_get_status()

  54. @coudenysj - jachim.be https://github.com/rlerdorf/opcache-status

  55. @coudenysj - jachim.be https://gist.github.com/ck-on/

  56. @coudenysj - jachim.be Opcache Priming / Deployment Strategies • opcache_compile_file()

    • FPM Pools • or…
  57. @coudenysj - jachim.be Preloading Preload PHP functions and classes once

    and use them in the context of any future request without overhead.
  58. @coudenysj - jachim.be Preload PHP functions and classes once and

    use them in the context of any future request without overhead.
  59. @coudenysj - jachim.be OPCache on steroids

  60. @coudenysj - jachim.be Preloading • PHP 7.4 • Part of

    opcache • Starts in master process before anything else • Loads code in memory "permanently" • No need to copy from shared memory to process memory
  61. @coudenysj - jachim.be Preloading • Opcache only works per file

    • Preloading helps with class libraries • Code will perform as internal entities (e.g. strlen, etc...) • Simple file with autoloading magic
  62. @coudenysj - jachim.be php.net/manual/en/opcache.configuration.php • opcache.preload=/path/to/preload.php • opcache_compile_file()

  63. @coudenysj - jachim.be Unlinked classes aren’t preloaded

  64. @coudenysj - jachim.be Preloading in the wild • github.com/brendt/laravel-preload/blob/master/preload.php •

    symfony.com/blog/new-in-symfony-4-4-preloading-symfony-applications-in-php -7-4 // var/cache/dev/srcApp_KernelDevDebugContainer.preload.php // This file has been auto-generated by the Symfony Dependency Injection Component // You can reference it in the "opcache.preload" php.ini setting on PHP >= 7.4 when preloading is desired use Symfony\Component\DependencyInjection\Dumper\Preloader; require dirname(__DIR__, 3).'/vendor/autoload.php'; require __DIR__.'/ContainerZxvZ783/srcApp_KernelDevDebugContainer.php'; $classes = []; $classes[] = 'App\Kernel'; Preloader::preload($classes);
  65. @coudenysj - jachim.be Caveats • “Full server” (fpm) ◦ php.ini

    setting • Server restart
  66. @coudenysj - jachim.be Composer • Sounds like a job for

    composer, right? • [RFC] Preloading support (https://github.com/composer/composer/issue s/7777)
  67. @coudenysj - jachim.be Composer benchmarks https://github.com/composer/composer/issues/7 777#issuecomment-440268416 • No preloading

    • Preloading only "hot" classes ◦ $files = opcache_get_status(true)['scripts']; • Preloading all the classes ◦ $files = require 'vendor/composer/autoload_classmap.php';
  68. @coudenysj - jachim.be Composer benchmarks https://github.com/composer/composer/issues/7777#issuecomment-440268416

  69. @coudenysj - jachim.be Composer at the moment https://github.com/composer/composer/issues/7777#issuecomment-559725760

  70. @coudenysj - jachim.be

  71. @coudenysj - jachim.be Preloading issues • There were some problems

    with using require() instead of opcache_compile_file() ◦ Should be fixed in latest release • Some issues in bugtracker, but not that bad ◦ https://bugs.php.net/search.php?cmd=display&search_for =preload
  72. @coudenysj - jachim.be Performance • Is it worth the effort?

    • Depends on the situation • We mostly see an improvement • Stick with the hot classes • Currently looking at how we can apply it at scale for our customers at Combell • Some reports: ◦ https://stitcher.io/blog/php-preload-benchmarks ◦ https://ezplatform.com/blog/php-7.4-opcache-preloading-benchmark ◦ https://developer.happyr.com/php-74-preload
  73. @coudenysj - jachim.be Preload resources • https://externals.io/message/103333 • https://wiki.php.net/rfc/preload •

    https://github.com/php/php-src/pull/3538
  74. @coudenysj - jachim.be In conclusion

  75. @coudenysj - jachim.be PHP Performance • Know enough of PHP

    internals • Know your application (use information functions) • Finetune • Repeat
  76. @coudenysj - jachim.be Elastic Stack @ Combell • Gather realpath/opcache/etc...

    data from all accounts • Act upon that data (which is changing constantly) • Autotuning & autoconfigure preload
  77. @coudenysj - jachim.be Thank you! @coudenysj - jachim.be