Slide 1

Slide 1 text

& Preloading A love story

Slide 2

Slide 2 text

Agenda •A short history of OPcache •Preloading, the theory •Preloading, the practice •Conclusion

Slide 3

Slide 3 text

A short history of OPcache

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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?

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

PHP 7.4 • Immutable opcodes stay in shared memory • And…

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

Out of scope • Bootstraping the kernel or equivalent • Loading the services (persistent connections) • Computing the response

Slide 12

Slide 12 text

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)

Slide 13

Slide 13 text

• BTW, constants are not preloaded Constants were not declared when preloading was used

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

Preloading The practice

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

opcache_compile_file()?

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

Composer to the rescue?

Slide 23

Slide 23 text

include() + autoloading? • Allows for complex loading strategies src/ vendor/ var/cache/

Slide 24

Slide 24 text

The obvious part: preload all services List all classes used by services (autoloading will cascade to parent classes/traits/interfaces)

Slide 25

Slide 25 text

Cascade to types used on the public API (skip protect/private, they lead to over-preloading)

Slide 26

Slide 26 text

List classes used by the implementation

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

List classes used by the implementation Exclude warmup-time services

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

• 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?

Slide 33

Slide 33 text

Conclusion The practice

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

Thank you! See you at Disneyland Paris in December or earlier hopefully!