Slide 1

Slide 1 text

High performance PHP8 at Scale Max Małecki Poznań, PHPers Summit 27 May 2023 Photo by Oscar Sutton on Unsplash

Slide 2

Slide 2 text

Photo by Drew Stock on Unsplash The Speed

Slide 3

Slide 3 text

Photo by toine G on Unsplash We all love

Slide 4

Slide 4 text

Photo by Jaxon Smith on Unsplash Performance

Slide 5

Slide 5 text

Photo by JOHN BEARBY IMAGES on Unsplash We all want

Slide 6

Slide 6 text

Photo by Bill Jelen on Unsplash Power

Slide 7

Slide 7 text

Source: https://opinion.al/video-sot-36-vjet-nga-vrasja-e-john-lennon/ Is all we need

Slide 8

Slide 8 text

Photo by Isaac Smith on Unsplash Expectations are so...

Slide 9

Slide 9 text

Photo by Dalton Smith on Unsplash High.

Slide 10

Slide 10 text

Photo by Dalton Smith on Unsplash But results may

Slide 11

Slide 11 text

Photo by Racim Amr on Unsplash Vary

Slide 12

Slide 12 text

Photo by David Armstrong on Unsplash Mediocore

Slide 13

Slide 13 text

Photo by Conor Samuel on Unsplash Harmfull Harmfull

Slide 14

Slide 14 text

Greetings!

Slide 15

Slide 15 text

I’m Max I’m from Poznań

Slide 16

Slide 16 text

For 17 years As Senior Backend Engenieer Since 2020

Slide 17

Slide 17 text

Yet another emgiezet/ errbitPHP Talked about technical debt and german cars. Boring speaker bullshit

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

2023? 2023? I’m Necromancer!

Slide 20

Slide 20 text

Desclimer: Desclimer: I practise what I preach. I practise what I preach. Example will be in Symfony 6 Example will be in Symfony 6 Sorry Tyler no Laravel in this presentation. Sorry Tyler no Laravel in this presentation.

Slide 21

Slide 21 text

Illustrations by Pixeltrue on icons8 0.We gonna talk about “the speed” theory; 1.How to find bottlenecks; 2.PHP Configuration tuning; 3.Every day coding good practices; 4.You will learn about benchmarks methodology; 5.We will watch some cool graphs 6.Apply performance boost in to your project. Today’s Agenda

Slide 22

Slide 22 text

Photo by Drew Stock on Unsplash The Speed

Slide 23

Slide 23 text

Distance traveled in given unit of time

Slide 24

Slide 24 text

v= d t

Slide 25

Slide 25 text

The Speed So how fast your application should respond?

Slide 26

Slide 26 text

The Speed Below 1 second? It is fast enough?

Slide 27

Slide 27 text

The Speed 100 milisecond? It is better?

Slide 28

Slide 28 text

The Speed Latency point of reference

Slide 29

Slide 29 text

L1 cache reference ......................... 0.5 ns Branch mispredict ............................ 5 ns L2 cache reference ........................... 7 ns Mutex lock/unlock ........................... 25 ns Main memory reference ...................... 100 ns Compress 1K bytes with Zippy ............. 3,000 ns = 3 µs Send 2K bytes over 1 Gbps network ....... 20,000 ns = 20 µs SSD random read ........................ 150,000 ns = 150 µs Read 1 MB sequentially from memory ..... 250,000 ns = 250 µs Round trip within same datacenter ...... 500,000 ns = 0.5 ms Read 1 MB sequentially from SSD* ..... 1,000,000 ns = 1 ms Disk seek ........................... 10,000,000 ns = 10 ms Read 1 MB sequentially from disk .... 20,000,000 ns = 20 ms Send packet CA->Netherlands->CA .... 150,000,000 ns = 150 ms from https://gist.github.com/hellerbarde/2843375

Slide 30

Slide 30 text

What the speed really mean? In web request context

Slide 31

Slide 31 text

Requests per second

Slide 32

Slide 32 text

v= r t

Slide 33

Slide 33 text

That gets a response with HTTP_CODE 2**

Slide 34

Slide 34 text

And Meaningfull content. Ofcourse.

Slide 35

Slide 35 text

What the speed really mean? in a consumer / worker context

Slide 36

Slide 36 text

Messages processed per second

Slide 37

Slide 37 text

Jobs finished per second

Slide 38

Slide 38 text

How to measure the speed?

Slide 39

Slide 39 text

Photo by Fotis Fotopoulos on Unsplash Benchmarking

Slide 40

Slide 40 text

Just like a unit tests benchmarks should be organized in suites.

Slide 41

Slide 41 text

Can you give us a example of totally useless benchmark?

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

The Speed PHPBench

Slide 44

Slide 44 text

PHPBench is a benchmark runner for PHP analogous to PHPUnit but for performance rather than correctness.

Slide 45

Slide 45 text

./vendor/bin/phpbench run tests/Benchmark -- report=default

Slide 46

Slide 46 text

PHPBench (1.2.10) running benchmarks... #standwithukraine with configuration file: /home/mgz/workspace/phpers/summit2023/8.1 Bench/phpbench.json with PHP version 8.0.28, xdebug , opcache ❌ ❌ \emgiezet\Tests\Benchmark\TimeConsumerBench benchConsume............................I4 - Mo152.444μs (±0.09%) Subjects: 1, Assertions: 0, Failures: 0, Errors: 0 +------+-------------------+--------------+-----+------+----------+-----------+--------------+----------------+ | iter | benchmark | subject | set | revs | mem_peak | time_avg | comp_z_value | comp_deviation | +------+-------------------+--------------+-----+------+----------+-----------+--------------+----------------+ | 0 | TimeConsumerBench | benchConsume | | 1000 | 704,944b | 152.652μs | +0.92σ | +0.08% | | 1 | TimeConsumerBench | benchConsume | | 1000 | 704,944b | 152.713μs | +1.38σ | +0.12% | | 2 | TimeConsumerBench | benchConsume | | 1000 | 704,944b | 152.390μs | -1.05σ | -0.09% | | 3 | TimeConsumerBench | benchConsume | | 1000 | 704,944b | 152.390μs | -1.05σ | -0.09% | | 4 | TimeConsumerBench | benchConsume | | 1000 | 704,944b | 152.503μs | -0.20σ | -0.02% | +------+-------------------+--------------+-----+------+----------+-----------+--------------+----------------+

Slide 47

Slide 47 text

With nice customizable HTML reports

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

What is limiting my application speed?

Slide 50

Slide 50 text

Photo by Andrew Seaman on Unsplash Bottlenecks

Slide 51

Slide 51 text

Bottleneck A bottleneck is a phenomenon where the performance and/or capacity of an entire system is limited by a single or limited number of components or resources.

Slide 52

Slide 52 text

Photo by John Cafazza on Unsplash Examples of Examples of

Slide 53

Slide 53 text

Populate 5 million items into ElasticSearch Index.

Slide 54

Slide 54 text

Cronjob that start to overlap the time frame of next execution

Slide 55

Slide 55 text

Elastica update listener on entity change in Doctrine make your insert / updates slow

Slide 56

Slide 56 text

You need to process 15GB XML import overnight

Slide 57

Slide 57 text

You need to serialize big fat entity with lots of relations

Slide 58

Slide 58 text

You want to host mongodb with db bigger than you available RAM

Slide 59

Slide 59 text

Photo by Dylan de Jonge on Unsplash Types of Types of

Slide 60

Slide 60 text

Photo by Dylan de Jonge on Unsplash Hardware Hardware

Slide 61

Slide 61 text

CPU

Slide 62

Slide 62 text

CPU overload

Slide 63

Slide 63 text

CPU Context switches

Slide 64

Slide 64 text

CPU IO waits

Slide 65

Slide 65 text

Memory

Slide 66

Slide 66 text

Out of Memory and go swap

Slide 67

Slide 67 text

Disk

Slide 68

Slide 68 text

Disk: Local disk access latency

Slide 69

Slide 69 text

Disk: Random disk I/O -> disk seeks

Slide 70

Slide 70 text

Disk: Disk fragmentation

Slide 71

Slide 71 text

Disk: SSDs performance drop once near full capacity

Slide 72

Slide 72 text

Photo by Dylan de Jonge on Unsplash Infrastructure Infrastructure

Slide 73

Slide 73 text

Network Long resolving DNS Lookups Dropped packets Faulty hardware

Slide 74

Slide 74 text

Virtualisation Bandwidth to and from the cloud provider Sharing a HDD, disk seek death Network I/O fluctuations in the cloud Enabling high-availability without accounting for failover

Slide 75

Slide 75 text

Database This slide can be separate presentation about database. Most popular: 1)Deadlocks, 2)Large joins taking up memory, 3)Long & short running queries

Slide 76

Slide 76 text

OS Fsync flushing, linux buffer cache filling up, File descriptor limits

Slide 77

Slide 77 text

Photo by Dylan de Jonge on Unsplash Application Application

Slide 78

Slide 78 text

Algorithm complexity

Slide 79

Slide 79 text

Threads

Slide 80

Slide 80 text

Deadlocks

Slide 81

Slide 81 text

No scalability

Slide 82

Slide 82 text

More bottlenecks?

Slide 83

Slide 83 text

How to catch them all?

Slide 84

Slide 84 text

Photo by Andrew Seaman on Unsplash Load Testing Load Testing

Slide 85

Slide 85 text

The goal is to check the performance of your system under a heavy load.

Slide 86

Slide 86 text

To push your system as far as it’s possible and check when it stop responding.

Slide 87

Slide 87 text

Photo: Sekurak.pl Your containers will Your containers will love it. love it.

Slide 88

Slide 88 text

Photo by Lachlan Donald on Unsplash Tools Tools

Slide 89

Slide 89 text

siege siege

Slide 90

Slide 90 text

siege -c 100 -r 100 -b

Slide 91

Slide 91 text

This will run 100 concurent sessions with 100 request each in benchmark mode, so with no delays between requests.

Slide 92

Slide 92 text

No content

Slide 93

Slide 93 text

{ "transactions": 10000, "availability": 100.00, "elapsed_time": 134.82, "data_transferred": 77.36, "response_time": 1.34, "transaction_rate": 74.17, "throughput": 0.57, "concurrency": 99.57, "successful_transactions": 10000, "failed_transactions": 0, "longest_transaction": 1.73, "shortest_transaction": 0.09 }

Slide 94

Slide 94 text

The Speed Apache Benchmark

Slide 95

Slide 95 text

ab -n 100 -c 100 http://localhost/en/blog/posts/aliquam-sodales- odio-id-eleifend-tristique

Slide 96

Slide 96 text

Benchmarking localhost (be patient).....done Server Software: nginx/1.17.8 Server Hostname: localhost Server Port: 80 Document Path: /en/blog/posts/aliquam-sodales-odio-id- eleifend-tristique Document Length: 31309 bytes

Slide 97

Slide 97 text

Concurrency Level: 100 Time taken for tests: 0.271 seconds Complete requests: 100 Failed requests: 0 Total transferred: 3186100 bytes HTML transferred: 3130900 bytes Requests per second: 369.54 [#/sec] (mean) Time per request: 270.604 [ms] (mean) Time per request: 2.706 [ms] (mean, across all concurrent requests) Transfer rate: 11498.08 [Kbytes/sec] received

Slide 98

Slide 98 text

Connection Times (ms) min mean[+/-sd] median max Connect: 0 1 0.2 1 2 Processing: 9 129 73.3 130 259 Waiting: 9 129 73.3 129 259 Total: 10 130 73.1 131 260

Slide 99

Slide 99 text

Percentage of the requests served within a certain time (ms) 50% 131 66% 171 75% 191 80% 205 90% 230 95% 249 98% 260 99% 260 100% 260 (longest request)

Slide 100

Slide 100 text

The Speed Gatling

Slide 101

Slide 101 text

https://github.com/gatling/gatling ● Written mostly in Scala ● Support load testing scenarios in scala/java/kotlin ● Have scenario recorder ● Needs own server to unleash its full capabilities ● Available also as paid SaaS: gatling.io

Slide 102

Slide 102 text

How to measure changes in my code performance?

Slide 103

Slide 103 text

Put benchmarking as additional CI step

Slide 104

Slide 104 text

Load testing is an overkill to put it in CI.

Slide 105

Slide 105 text

How to find a bottlenecks in my application?

Slide 106

Slide 106 text

Photo by Fotis Fotopoulos on Unsplash Profiling

Slide 107

Slide 107 text

What actually profiling is?

Slide 108

Slide 108 text

Profiling checking the entire callstack for given part of code. Each function is listed with: execution time and amount of calls.

Slide 109

Slide 109 text

XHProf

Slide 110

Slide 110 text

Installation *Example can be found in the presentation repository

Slide 111

Slide 111 text

RUN curl "http://pecl.php.net/get/xhprof-2.3.2.tgz" -fsL -o ./xhprof- 2.3.2.tgz && \ mkdir /var/xhprof && tar xf ./xhprof-2.3.2.tgz -C /var/xhprof && \ cd /var/xhprof/xhprof-2.3.2/extension && \ phpize && \ ./configure && \ make && \ make install # custom settings for xhprof COPY ./xhprof.ini /usr/local/etc/php/conf.d/xhprof.ini RUN docker-php-ext-enable xhprof #folder for xhprof profiles (same as in file xhprof.ini) RUN mkdir -m 777 /profiles Add it to your Dockerfile

Slide 112

Slide 112 text

How to use it

Slide 113

Slide 113 text

#[Route('/', name: 'blog_index', defaults: ['page' => '1', '_format' => 'html'], methods: ['GET'])] #[Route('/rss.xml', name: 'blog_rss', defaults: ['page' => '1', '_format' => 'xml'], methods: ['GET'])] #[Route('/page/{page<[1-9]\d{0,8}>}', name: 'blog_index_paginated', defaults: ['_format' => 'html'], methods: ['GET'])] #[Cache(smaxage: 10)] public function index(Request $request, int $page, string $_format, PostRepository $posts, TagRepository $tags): Response { xhprof_enable(XHPROF_FLAGS_MEMORY + XHPROF_FLAGS_CPU); $tag = null; if ($request->query->has('tag')) { $tag = $tags->findOneBy(['name' => $request->query->get('tag')]); } $latestPosts = $posts->findLatest($page, $tag); file_put_contents('/profiles/'.time().'.application.xhprof', serialize(xhprof_disable())); // Every template name also has two extensions that specify the format and // engine for that template. // See https://symfony.com/doc/current/templates.html#template-naming return $this->render('blog/index.'.$_format.'.twig', [ 'paginator' => $latestPosts, 'tagName' => $tag?->getName(), ]); }

Slide 114

Slide 114 text

Open xhprof web

Slide 115

Slide 115 text

Select the run

Slide 116

Slide 116 text

Select function

Slide 117

Slide 117 text

Pros ● Can be used with production settings. ● Can profile only couple lines of code.

Slide 118

Slide 118 text

Tip of the day. ● If you like your SSD life span turn off profiling while load testing ;)

Slide 119

Slide 119 text

Other options?

Slide 120

Slide 120 text

xdebug

Slide 121

Slide 121 text

Symfony Profiler

Slide 122

Slide 122 text

No content

Slide 123

Slide 123 text

No content

Slide 124

Slide 124 text

How to improve app response speed?

Slide 125

Slide 125 text

Photo by Fotis Fotopoulos on Unsplash Caching

Slide 126

Slide 126 text

How the load will look like when app hasn’t any cache?

Slide 127

Slide 127 text

No content

Slide 128

Slide 128 text

2 Goals:

Slide 129

Slide 129 text

1. Response cache: should prevent duplicated requests to reach the php-fpm.

Slide 130

Slide 130 text

2. Result cache: should prevent cached queries to reach the database.

Slide 131

Slide 131 text

If the cache entry has valid TTL.

Slide 132

Slide 132 text

Cache keys should have semantic naming

Slide 133

Slide 133 text

Because

Slide 134

Slide 134 text

There are only two hard things in Computer Science: cache invalidation and naming things. - Phil Karlton

Slide 135

Slide 135 text

Check your headers! Cache-Control: max-age=604800, must-revalidate

Slide 136

Slide 136 text

REST API GET endpoint responses can be cached

Slide 137

Slide 137 text

Database is last thing that should be called.

Slide 138

Slide 138 text

Prioritize endpoints caching by load they generate.

Slide 139

Slide 139 text

Photo by Kelly Sikkema on Unsplash Intro done Now we’re on the same page

Slide 140

Slide 140 text

Photo by Kyle Mackie on Unsplash It’s time for the main course

Slide 141

Slide 141 text

Photo by shri on Unsplash or for the tofu course To don’t offend anybody today.

Slide 142

Slide 142 text

PHP Configuration

Slide 143

Slide 143 text

memory_limit=1024M opcache.enable=1 opcache.revalidate_freq=10 opcache.validate_timestamps=1 opcache.max_accelerated_files=10000 opcache.memory_consumption=192 opcache.max_wasted_percentage=10 opcache.interned_strings_buffer=1 opcache.fast_shutdown=1 opcache.jit_buffer_size=256M opcache.jit=1255 opcache.jit=tracing upload_max_filesize = 20M post_max_size = 20M pm = ondemand pm.max_children = 500 pm.max_requests = 500

Slide 144

Slide 144 text

Composer

Slide 145

Slide 145 text

No content

Slide 146

Slide 146 text

/usr/bin/composer install \ --no-progress \ --no-interaction \ --no-dev \ --optimize-autoloader \ --prefer-dist

Slide 147

Slide 147 text

composer dump-autoload --classmap- authoritative

Slide 148

Slide 148 text

Or put this to APCu

Slide 149

Slide 149 text

composer dump-autoload --apcu

Slide 150

Slide 150 text

OPCache

Slide 151

Slide 151 text

Installation apt-get install php-opcache

Slide 152

Slide 152 text

Configuration #/etc/php8.1/fpm/php.ini opcache.enable=1 opcache.memory_consumption=128 opcache.max_accelerated_files=10000 opcache.revalidate_freq=200

Slide 153

Slide 153 text

Restart php-fpm

Slide 154

Slide 154 text

Add some extra preloading on fpm startup composer require practically/preloader

Slide 155

Slide 155 text

practically/preloader hooks after install and generates preload.php executable file.

Slide 156

Slide 156 text

update php.ini ;/etc/php8.1/fpm/php.ini opcache.enable=1 opcache.enable_cli=1 opcache.memory_consumption=128 opcache.max_accelerated_files=10000 opcache.revalidate_freq=200 opcache.preload=/path/to/preload.php opcache.preload_user=user

Slide 157

Slide 157 text

JIT Compiler

Slide 158

Slide 158 text

opcache.jit_buffer_size=256M opcache.jit=1255 opcache.jit=tracing

Slide 159

Slide 159 text

JIT Modes ● opcache.jit = 1205 - all code is JIT compiled ● opcache.jit = 1235 - only selected code portions (based on their relative use) are passed to the JIT compilation ● opcache.jit = 1255 - application code is tracked for compilation by JIT and selected parts of the code are transferred to the compiler

Slide 160

Slide 160 text

Photo by Fotis Fotopoulos on Unsplash Every day coding good practices

Slide 161

Slide 161 text

Use isset() to check that array is initialized

Slide 162

Slide 162 text

\emgiezet\Tests\Benchmark\IssetBench benchIsset..............................I0 - Mo0.012μs (±0.00%) benchSizeof.............................I0 - Mo0.021μs (±0.00%) benchCount..............................I0 - Mo0.021μs (±0.00%)

Slide 163

Slide 163 text

Stick with single quotes

Slide 164

Slide 164 text

\emgiezet\Tests\Benchmark\QuotesBench benchSingle.............................I0 - Mo0.445μs (±0.00%) benchDouble.............................I0 - Mo2.498μs (±0.00%)

Slide 165

Slide 165 text

RegExp’s are sexy but you need to pay

Slide 166

Slide 166 text

\emgiezet\Tests\Benchmark\PregBench benchPregReplace........................I0 - Mo3.906μs (±0.00%) benchStrReplace.........................I0 - Mo2.832μs (±0.00%)

Slide 167

Slide 167 text

Try identical operator instead of equals

Slide 168

Slide 168 text

\emgiezet\Tests\Benchmark\EqualsBench benchEqual..............................I0 - Mo0.305μs (±0.00%) benchIdentical..........................I0 - Mo0.301μs (±0.00%)

Slide 169

Slide 169 text

When you need precision math use BCMath but be aware that it costs double

Slide 170

Slide 170 text

\emgiezet\Tests\Benchmark\MathBench benchBcmul..............................I0 - Mo0.492μs (±0.00%) benchMultiply...........................I0 - Mo0.258μs (±0.00%)

Slide 171

Slide 171 text

Forget about other loops than foreach()

Slide 172

Slide 172 text

\emgiezet\Tests\Benchmark\LoopsBench benchFor................................I0 - Mo7.168ms (±0.00%) benchForeach............................I0 - Mo6.791ms (±0.00%) benchWhile..............................I0 - Mo7.153ms (±0.00%) benchDoWhile............................I0 - Mo7.056ms (±0.00%)

Slide 173

Slide 173 text

If you want more benchmarks like those go: https://phpbench.com/

Slide 174

Slide 174 text

If you want to run my benchmarks yourself go: https://github.com/emgiezet/ phpers-summit-2023

Slide 175

Slide 175 text

Find balance between Lazy & Eager loading in Doctrine

Slide 176

Slide 176 text

Use Generators & Iterators

Slide 177

Slide 177 text

Trust me. Check @alexdaubois presentation at 13:45

Slide 178

Slide 178 text

Remember of batch processing

Slide 179

Slide 179 text

Partition your cli commands

Slide 180

Slide 180 text

Never try to load XML ’s bigger than 50 MB to DOMObject

Slide 181

Slide 181 text

Don’t spam logs. Each echo() each print() costs.

Slide 182

Slide 182 text

Photo by Fotis Fotopoulos on Unsplash Benchmark time!

Slide 183

Slide 183 text

Benchmark App ● Symfony Demo App – In a docker container – Ngnix & php-fpm – In prod mode – Whitout xdebug – Mysql isntead of sqlite – It has symfony.cache enabled (#[Cache(smaxage: 10)])

Slide 184

Slide 184 text

No content

Slide 185

Slide 185 text

What we gonna benchmark? ● 8.1 ● 8.1 Opcache ● 8.1 Opcache + JIT compiler enabled

Slide 186

Slide 186 text

How we gonna benchmark? ● Siege on multiple urls siege -c 100 -r 100 -b -i -f urls.txt

Slide 187

Slide 187 text

PHP 8.1 – no opcache { "transactions": 66730, "availability": 100.00, "elapsed_time": 149.54, "data_transferred": 5511.54, "response_time": 0.22, "transaction_rate": 446.24, "throughput": 36.86, "concurrency": 99.45, "successful_transactions": 66730, "failed_transactions": 0, "longest_transaction": 1.75, "shortest_transaction": 0.00 }

Slide 188

Slide 188 text

PHP 8.1 – opcache { "transactions": 66988, "availability": 100.00, "elapsed_time": 24.26, "data_transferred": 5535.86, "response_time": 0.04, "transaction_rate": 2761.25, "throughput": 228.19, "concurrency": 99.38, "successful_transactions": 66988, "failed_transactions": 0, "longest_transaction": 0.40, "shortest_transaction": 0.00 }

Slide 189

Slide 189 text

PHP 8.1 – opcache & jit { "transactions": 66724, "availability": 100.00, "elapsed_time": 19.69, "data_transferred": 5510.57, "response_time": 0.03, "transaction_rate": 3388.73, "throughput": 279.87, "concurrency": 99.36, "successful_transactions": 66724, "failed_transactions": 0, "longest_transaction": 0.45, "shortest_transaction": 0.00 }

Slide 190

Slide 190 text

Req/s boost Php 8.1 Opcache Enabled Opcache and JIT Enabled 0 500 1000 1500 2000 2500 3000 3500 4000 PHP 8.1 Performance Request / s

Slide 191

Slide 191 text

Photo by Brands&People on Unsplash What you can What you can apply to your apply to your project? project?

Slide 192

Slide 192 text

No content

Slide 193

Slide 193 text

Photo by Azin Javadzadeh on Unsplash Greenfield

Slide 194

Slide 194 text

Manage the performance expectations

Slide 195

Slide 195 text

Run loadtests after each delivery cycle.

Slide 196

Slide 196 text

Add benchmarks to your CI pipeline.

Slide 197

Slide 197 text

Try to align your benchmark scenarios with KPI that businesses wants.

Slide 198

Slide 198 text

Take baby steps

Slide 199

Slide 199 text

Don’t over-engineer

Slide 200

Slide 200 text

No content

Slide 201

Slide 201 text

Add static code analysis. With performance rules check.

Slide 202

Slide 202 text

Always consider performance impact on feature planning.

Slide 203

Slide 203 text

Always consider performance impact on code review.

Slide 204

Slide 204 text

First implementation is always rubbish.

Slide 205

Slide 205 text

Photo by Brett Jordan on Unsplash Legacy Legacy

Slide 206

Slide 206 text

Set up performance goals.

Slide 207

Slide 207 text

Find bottlenecks.

Slide 208

Slide 208 text

Prioritize them with businesses.

Slide 209

Slide 209 text

Decisions should be supported by real data.

Slide 210

Slide 210 text

Plan them. Apply one change at the time and then benchmark!

Slide 211

Slide 211 text

And… Repeat from the first step.

Slide 212

Slide 212 text

No content

Slide 213

Slide 213 text

Photo by Fotis Fotopoulos on Unsplash Any Questions?

Slide 214

Slide 214 text

Catch me! ● Code from presentation: https://github.com/emgiezet/phpers-summit- 2023 ● Twitter: @mgz

Slide 215

Slide 215 text

Photo by Kelly Sikkema on Unsplash