Preloading and Symfony, a love story

Preloading and Symfony, a love story

Preloading is a hot topic these days, yet the first versions of PHP 7.4 crashed when it was enabled. Since September with the first experiments (and crash reports), Nicolas is tracking the beast: PHP 7.4.5 finally makes it usable in all contexts and a 75% boost has been measured on a "Hello World" app. Can you expect the same benefits on your apps? Preloading comes with its own challenges. Let's review them and figure how you can get the most out of it for the benefit of your server's efficiency and response time.

First presented at https://online.live.symfony.com/

6baa34bc1e5c347b1003f6abe8691de1?s=128

Nicolas Grekas

April 17, 2020
Tweet

Transcript

  1. & Preloading A love story

  2. Agenda •A short history of OPcache •Preloading, the theory •Preloading,

    the practice •Conclusion
  3. A short history of OPcache

  4. Lambda-style before it was hype • PHP has a run-and-forget

    execution model • Every request has to reload everything all-the-time • Memory leaks and shared state are hard problems • This is great for developer efficiency • This is great for robustness of apps • This makes hosting great • This makes scalability easy
  5. Shared memory (SHM) to the rescue • The code is

    immutable • The compilation steps can be cached • APC, eAccelerator, Turck MMCache, Zend OpCache FTW • But which compilation steps?
  6. Opcode caches • The source code is compiled into a

    list of instructions, the opcodes: • ADD, CONCAT, ASSIGN, …, DECLARE_CLASS, DECLARE_FUNCTION, DECLARE_INHERITED_CLASS, ADD_INTERFACE, etc. • Shared memory holds a hashmap map of (file => opcodes) pairs
  7. Opcode caches++ • PHP 5.4: interned strings • PHP 5.5:

    OPcache becomes open-source and builtin • PHP 5.6: array declarations stay in shared memory • PHP 7.0: • all static arrays stay in shared memory • post-compilation optimizations • PHP 7.1, 7.2, 7.3 • more post-compilation optimizations
  8. What remains before 7.4? • Check freshness • Transfer opcodes

    into each request’s memory • Link parent classes, interfaces and traits • Validate signatures, covariance and contravariance
  9. PHP 7.4 • Immutable opcodes stay in shared memory •

    And…
  10. Preloading The theory, from https://wiki.php.net/rfc/preload

  11. Out of scope • Bootstraping the kernel or equivalent •

    Loading the services (persistent connections) • Computing the response
  12. Preloading removes • Freshness checks • Everything related to loading

    and declaring classes and functions • Everything is available out-of-the-box, feels like native • (RIP function autoloading, you’ll never have any purpose)
  13. • BTW, constants are not preloaded Constants were not declared

    when preloading was used
  14. Preloading 101 • opcache.preload=/some/preloading/script.php in your php.ini opcache.preload_user=www-data if running

    as root (Docker anyone?) • Any classes or functions included by this script Any classes or functions opcache_compile_file()’ed • php-fpm / php -S will load them before serving any requests
  15. (symbol => opcodes) pairs in SHM • Incompatible with several

    apps on the same FPM server • Is really immutable: reboot the server to reload the code • opcache_get_status() gives all the info • Declarations nested in « if » statements are not preloaded • Only fully-resolved classes are preloaded (you can ignore the warnings) • (oh, it’s not available on Windows)
  16. Preloading The practice

  17. Which strategy for your preload.php? • opcache_compile_file() or • include()

    + autoloading?
  18. opcache_compile_file()?

  19. https://github.com/symfony/symfony/pull/32032

  20. https://github.com/composer/composer/issues/7777

  21. opcache_compile_file()? 1. Deploy without preloading 2. Get some HTTP traffic

    3. Dump preload.php from opcache_get_status() 4. Restart prod with preloading 5. Automate for every deployment • Still loads too many classes (that’s theory) • Not practical – requires advanced tooling • Not compatible with class_alias()
  22. Composer to the rescue?

  23. include() + autoloading? • Allows for complex loading strategies src/

    vendor/ var/cache/
  24. The obvious part: preload all services List all classes used

    by services (autoloading will cascade to parent classes/traits/interfaces)
  25. Cascade to types used on the public API (skip protect/private,

    they lead to over-preloading)
  26. List classes used by the implementation

  27. None
  28. List classes used by the implementation Exclude warmup-time services

  29. List cache warmer artifacts (e.g. compiled Twig templates, annotation classes,

    etc.)
  30. None
  31. include() + autoloading! • The configuration drives the useful classes

    • Inline class_exists() declare local sidekicks • container.preload/.no_preload for fine tuning • Make cache warmers report their artifacts
  32. • opcache.preload=var/cache/prod/App_KernelProdContainer.preload.php • Profit • (optionally, fine tune with opcache_get_status()

    and commit the result) • (go patch open-source bundles and libs to maximize their preloading potential) include() + autoloading?
  33. Conclusion The practice

  34. Kudos Dmitry and Nikita • include() + autoloading • opcache_compile_file()

    • Hello World with Twig and without preloading: 360 req/s • with preloading, no cache warmers: 560 req/s (+55%) • with preloading, yes cache warmers: 630 req/s (+75%) • A typical app spends 15% loading code • It’s worth it! (but not worth a complex deployment process) PS: there is no memory to share when running on the CLI PPS: autoloading + Composer FTW and here to stay
  35. Thank you! See you at Disneyland Paris in December or

    earlier hopefully!