$30 off During Our Annual Pro Sale. View Details »

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.

Jachim Coudenys

January 24, 2020
Tweet

More Decks by Jachim Coudenys

Other Decks in Technology

Transcript

  1. @coudenysj - jachim.be
    PHP OPCache, Realpath
    Cache and Preloading
    How to tune your PHP installation

    View Slide

  2. @coudenysj - jachim.be
    New PHP versions go
    faster, but we can
    make them go even
    faster!

    View Slide

  3. @coudenysj - jachim.be
    Some groundwork

    View Slide

  4. @coudenysj - jachim.be
    PHP
    ● Scripting Language
    ● Fire and forget
    ● No "manual" compilation

    View Slide

  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

    View Slide

  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.

    View Slide

  7. @coudenysj - jachim.be
    All set?

    View Slide

  8. @coudenysj - jachim.be
    Jachim Coudenys

    View Slide

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

    View Slide

  10. @coudenysj - jachim.be
    Right!

    View Slide

  11. @coudenysj - jachim.be
    Realpath Cache

    View Slide

  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

    View Slide

  13. @coudenysj - jachim.be
    php.net/manual/en/ref.filesystem.php
    ● realpath_cache_get()
    ● realpath_cache_size()
    ● realpath()

    View Slide

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

    View Slide

  15. @coudenysj - jachim.be
    $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());

    View Slide

  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
    )

    View Slide

  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
    )

    View Slide

  18. @coudenysj - jachim.be
    Realpath Cache: what
    can we tune?

    View Slide

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

    View Slide

  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.

    View Slide

  21. @coudenysj - jachim.be
    https://engineering.facile.it/blog/eng/realpath-cache-is-it-
    all-php-opcache-s-fault/

    View Slide

  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

    View Slide

  23. @coudenysj - jachim.be
    https://engineering.facile.it/blog/eng/realpath-cache-is-it-
    all-php-opcache-s-fault/

    View Slide

  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.

    View Slide

  25. @coudenysj - jachim.be
    Vulcan Logic Dumper
    https://3v4l.org/A1B4H/vld

    View Slide

  26. @coudenysj - jachim.be
    This can get big quite quickly
    ● Libraries
    ● Frameworks
    ● etc...

    View Slide

  27. @coudenysj - jachim.be
    OPCodes (usually) don't change
    ● Let’s add in some cache!

    View Slide

  28. @coudenysj - jachim.be
    OPCache
    ● Bunch of opcode cachers
    ○ APC
    ○ Turck MMCache
    ○ Zend Optimizer
    ○ etc...
    ● Zend Optimizer "donated" by Zend
    ● Since PHP 5.5

    View Slide

  29. @coudenysj - jachim.be
    https://engineering.facile.it/blog/eng/realpath-cache-is-it-
    all-php-opcache-s-fault/

    View Slide

  30. @coudenysj - jachim.be
    Shared memory
    ● Stored in the master process of FPM

    View Slide

  31. @coudenysj - jachim.be
    OPCache Optimizer
    ● Gets better every release
    ● Optimizes:
    ○ Branches
    ○ Dead code
    ○ ++$a vs $a++

    View Slide

  32. @coudenysj - jachim.be
    Optimized opcodes (example)
    https://3v4l.org/A1B4H/vld

    View Slide

  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()

    View Slide

  34. @coudenysj - jachim.be
    demo
    php opcache.php

    View Slide

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

    View Slide

  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
    )
    ...

    View Slide

  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
    )
    ...

    View Slide

  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
    )
    )
    ...

    View Slide

  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)

    View Slide

  40. @coudenysj - jachim.be
    Keys
    ● Full path to file
    ● Relative paths to files

    View Slide

  41. @coudenysj - jachim.be
    Wasted memory
    ● Opcache doesn't do "defragmentation"
    ● File changes cause recompilation

    View Slide

  42. @coudenysj - jachim.be
    Opcache restarts
    ● OOM Restarts
    ○ Memory
    ● Hash Restarts
    ○ Keys
    ● Manual Restarts
    ○ opcache_reset()

    View Slide

  43. @coudenysj - jachim.be
    NEVER have a full cache!

    View Slide

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

    View Slide

  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

    View Slide

  46. @coudenysj - jachim.be
    demo
    php -d opcache.enable_cli=On -d
    opcache.file_cache="/tmp/opcache"
    opcache.php

    View Slide

  47. @coudenysj - jachim.be
    // php -d opcache.enable_cli=On -d
    opcache.file_cache="/home/jachim/demo/cache" opcache.php
    print_r(opcache_get_status());

    View Slide

  48. @coudenysj - jachim.be
    └╼ tree cache
    cache
    └── 26f7903bdd40555497c24242ea455a66
    └── home
    └── jachim
    └── demo
    └── opcache.php.bin
    4 directories, 1 file

    View Slide

  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.

    View Slide

  50. @coudenysj - jachim.be
    OPCache: what can
    we tune?

    View Slide

  51. @coudenysj - jachim.be
    “Disaster Recovery” Scenarios
    ● Memory Full?
    ● Interned Strings Full?
    ● Key store full?

    View Slide

  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

    View Slide

  53. @coudenysj - jachim.be
    Getting stats
    ● opcache_get_status()

    View Slide

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

    View Slide

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

    View Slide

  56. @coudenysj - jachim.be
    Opcache Priming / Deployment Strategies
    ● opcache_compile_file()
    ● FPM Pools
    ● or…

    View Slide

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

    View Slide

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

    View Slide

  59. @coudenysj - jachim.be
    OPCache on steroids

    View Slide

  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

    View Slide

  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

    View Slide

  62. @coudenysj - jachim.be
    php.net/manual/en/opcache.configuration.php
    ● opcache.preload=/path/to/preload.php
    ● opcache_compile_file()

    View Slide

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

    View Slide

  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);

    View Slide

  65. @coudenysj - jachim.be
    Caveats
    ● “Full server” (fpm)
    ○ php.ini setting
    ● Server restart

    View Slide

  66. @coudenysj - jachim.be
    Composer
    ● Sounds like a job for composer, right?
    ● [RFC] Preloading support
    (https://github.com/composer/composer/issue
    s/7777)

    View Slide

  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';

    View Slide

  68. @coudenysj - jachim.be
    Composer benchmarks
    https://github.com/composer/composer/issues/7777#issuecomment-440268416

    View Slide

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

    View Slide

  70. @coudenysj - jachim.be

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  74. @coudenysj - jachim.be
    In conclusion

    View Slide

  75. @coudenysj - jachim.be
    PHP Performance
    ● Know enough of PHP internals
    ● Know your application (use information
    functions)
    ● Finetune
    ● Repeat

    View Slide

  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

    View Slide

  77. @coudenysj - jachim.be
    Thank you!
    @coudenysj - jachim.be

    View Slide