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

PHP OPCache, Realpath Cache and Preloading (PHPUK 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

February 21, 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 Why this presentation?

  12. @coudenysj - jachim.be 80% of performance issues have nothing to

    do with your server! https://www.combell.com/en/performance-team
  13. @coudenysj - jachim.be Latency Comparison Numbers (~2012) L1 cache reference

    0.5 ns Branch mispredict 5 ns L2 cache reference 7 ns 14x L1 cache Main memory reference 100 ns 20x L2 cache, 200x L1 Send 1K bytes over 1 Gbps network 10,000 ns 10 us Read 4K randomly from SSD* 150,000 ns 150 us ~1GB/sec SSD Read 1 MB sequentially from memory 250,000 ns 250 us Round trip within same datacenter 500,000 ns 500 us Read 1 MB sequentially from SSD* 1,000,000 ns 1,000 us 1 ms ~1GB/sec SSD, 4X memory Disk seek 10,000,000 ns 10,000 us 10 ms 20x datacenter roundtrip Read 1 MB sequentially from disk 20,000,000 ns 20,000 us 20 ms 80x memory, 20X SSD Send packet CA->Netherlands->CA 150,000,000 ns 150,000 us 150 ms
  14. @coudenysj - jachim.be https://gist.github.com/jboner/2841832

  15. @coudenysj - jachim.be How can we fix it?

  16. @coudenysj - jachim.be Disk IO

  17. @coudenysj - jachim.be

  18. @coudenysj - jachim.be

  19. @coudenysj - jachim.be

  20. @coudenysj - jachim.be Network File System (NFS)

  21. @coudenysj - jachim.be

  22. @coudenysj - jachim.be Tackle these challenges in PHP Realpath Cache

    OPCache Preloading
  23. @coudenysj - jachim.be Realpath Cache

  24. @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
  25. @coudenysj - jachim.be php.net/manual/en/ref.filesystem.php • realpath_cache_get() • realpath_cache_size() • realpath()

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

  27. @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());
  28. @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 )
  29. @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 )
  30. @coudenysj - jachim.be Realpath Cache: what can we tune?

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

  32. @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.
  33. @coudenysj - jachim.be https://engineering.facile.it/blog/eng/realpath-cache-is-it- all-php-opcache-s-fault/

  34. @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
  35. @coudenysj - jachim.be https://engineering.facile.it/blog/eng/realpath-cache-is-it- all-php-opcache-s-fault/

  36. @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.
  37. @coudenysj - jachim.be Vulcan Logic Dumper https://3v4l.org/A1B4H/vld

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

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

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

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

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

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

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

  45. @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()
  46. @coudenysj - jachim.be demo php opcache.php

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

  48. @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 ) ...
  49. @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 ) ...
  50. @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 ) ) ...
  51. @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)
  52. @coudenysj - jachim.be Keys • Full path to file •

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

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

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

  56. @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
  57. @coudenysj - jachim.be demo php -d opcache.enable_cli=On -d opcache.file_cache="/tmp/opcache" opcache.php

  58. @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());
  59. @coudenysj - jachim.be └╼ tree cache cache └── 26f7903bdd40555497c24242ea455a66 └──

    home └── jachim └── demo └── opcache.php.bin 4 directories, 1 file
  60. @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.
  61. @coudenysj - jachim.be OPCache: what can we tune?

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

    Interned Strings Full? • Key store full?
  63. @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
  64. @coudenysj - jachim.be Getting stats • opcache_get_status()

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

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

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

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

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

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

  71. @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
  72. @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
  73. @coudenysj - jachim.be php.net/manual/en/opcache.configuration.php • opcache.preload=/path/to/preload.php • opcache_compile_file()

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

  75. @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);
  76. @coudenysj - jachim.be Caveats • “Full server” (fpm) ◦ php.ini

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

    composer, right? • [RFC] Preloading support (https://github.com/composer/composer/issue s/7777)
  78. @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';
  79. @coudenysj - jachim.be Composer benchmarks https://github.com/composer/composer/issues/7777#issuecomment-440268416

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

  81. @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
  82. @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
  83. @coudenysj - jachim.be Preload resources • https://externals.io/message/103333 • https://wiki.php.net/rfc/preload •

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

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

    internals • Know your application (use information functions) • Finetune • Repeat
  86. @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
  87. @coudenysj - jachim.be Thank you! @coudenysj - jachim.be https://joind.in/talk/d1cfa