PHP Roundtable #38

Hoszt:

Naményi Dávid (@NamenyiDavid)

Sági-Kazár Márk (@sagikazarmark)

Kocsis Máté (@kocsismate90)

Támogatónk:

  • Optimizing the performance of PHP apps

  • Instrumenting PHP applications

Mai témák

Optimizing the performance of PHP applications

sssss

ssss

ssss

 

Aspects of performance

Speed vs. Scalability

How to optimize perf?

Measure, Act, Measure

Tools for Measuring

  • AB, Loader.io

  • New Relic, Grafana

  • Blackfire, XHProf

  • PHPBench

  • VLD

What to optimize?

  • Database Access

  • Algorithms

  • Dependencies

  • PHP & Config

Database Access

Lazy-Load PDO

PDO starts the DB connection as soon as it is instantiated

N+1 Query Problem

<?php

$cities = City::get();

foreach ($cities as $city) {
    $city->country->name;
}

N+1 Query Problem

<?php

$cities = City::with("country")->get();

Algorithms

Mind Time Complexity

Sign Name Good/Bad
O(1) Constant Time Perfect
O(log n) Logarithmic Time Very good
O(n) Linear Time Good
O(n^2) Quadratic Time Avoid
2^O(n) Exponential Time Panic

Constant Time - O(1)

<?php

$numbers = [1, 2, 3, 4, 5];

echo $numbers[2];

Logarithmic Time - O(log n)

Binary Search

Linear Time - O(n)

<?php

$numbers = [1, 2, 3, 4, 5];

foreach ($numbers as $number) {
    echo $number;
}

Quadratic Time - O(n^2)

<?php

$numbers1 = [1, 2, 3, 4, 5];
$numbers2 = [3, 4, 5, 6, 7];

foreach ($numbers1 as $i) {
    foreach ($numbers2 as $j) {
        if ($i === $j) {
            echo $i;
        }
    }
}

Quadratic Time - O(n^2)

<?php

$numbers1 = [1, 2, 3, 4, 5];
$numbers2 = [3, 4, 5, 6, 7];

// [3 => 0, 4 => 1, 5 => 2, 6 => 3, 7 => 4]
$numbers2Map = array_flip($numbers2);

foreach ($numbers1 as $number) {
    if (isset($numbers2Map[$number])) {
        echo $number;
    }
}

Quadratic Time - O(n^2)

<?php

$numbers1 = [1, 2, 3, 4, 5];
$numbers2 = [3, 4, 5, 6, 7];

// [3, 4, 5]
$numbers = array_intersect(
    $numbers1,
    $numbers2
);

foreach ($numbers as $number) {
    echo $number;
}

Exponential Time - 2^O(n)

The Travelling Salesman Problem

Dependencies

Use a Fast Framework

<?php

$harmony = new Harmony(ServerRequestFactory::fromGlobals(), new Response());
$harmony
    ->addMiddleware(new HttpHandlerRunnerMiddleware(new SapiEmitter()))
    ->addMiddleware(new FastRouteMiddleware($router))
    ->addMiddleware(new DispatcherMiddleware())
    ->run();

Use a Fast Router

Use a Fast DI Container

PHP Configuration

Upgrade PHP

Upgrade PHP

PHP Engine overview

OPCache Tuning

[OPCACHE]
zend-extension=opcache
opcache.consistency_checks=0
opcache.enable=1
opcache.enable_cli=1
opcache.enable_file_override=1
opcache.fast_shutdown=1
opcache.max_accelerated_files=60000
opcache.memory_consumption=128
opcache.validate_timestamps=0
opcache.revalidate_freq=8
opcache.revalidate_path=0
opcache.save_comments=1
opcache.use_cwd=0
opcache.file_update_protection=0
opcache.interned_strings_buffer=32

Preloading

[OPCACHE]
...
opcache.preload=/var/www/preload.php
<?php

opcache_compile_file('/var/www/src/Class1.php');
opcache_compile_file('/var/www/src/Class2.php');
opcache_compile_file('/var/www/src/Class3.php');
// ...

PHP Micro-optimizations

Optimize autoloading

composer install \
    --no-interaction \
    --no-suggest \
    --no-dev \
    --no-scripts \
    --prefer-dist \
    --optimize-autoloader \
    --classmap-authoritative

Optimize autoloading

<?php

// ------------ Slower ---------------

require_once("vendor/autoload.php");

// ------------ Faster ---------------

require_once("src/Class1.php");

Use these PHP 5.6 features

<?php

class Class1
{
    public const $items = [
        "Class1" => "/var/www" . "/src/Class1.php",
    ];
    
    public static $items = [
        "Class2" => __DIR__ . "/src/Class1.php",
    ];
}
  • constant expressions

  • interned strings

  • immutable arrays

Use these PHP 5.6 features

Omit types

<?php

// -------------------------------------- Slower ----------------------------------

function funct2(string $a, int $b, array $c, stdClass $d, float $e, bool $f): void
{
    // ...
}

// -------------------------------------- Faster ----------------------------------

/**
 * @param string $a
 * @param int $b
 * @param array $c
 * @param stdClass $d
 * @param float $e
 * @param bool $f
 * @return void
 */
function funct1($a, $b, $c, $d, $e, $f)
{
    // ...
}

It's also a great way to tackle visual debt!

Omit types

Import functions

namespace App;

// ------- Slower ----------

count([]);

// ------- Faster ----------

\count();
use function count;

Import functions

Omit function calls

<?php

// ---------- Slower -------------

array_key_exists("key", $array);

// ---------- Faster -------------

isset($array["key"]);

Omit function calls

But...

Use arrays wisely

Use Closure binding

<?php

$kitchen = new Kitchen();

// ---------------------- Slower --------------------------

$sweetsThief = new ReflectionProperty("Kitchen", "yummy");
$sweetsThief->setAccessible(true);

// ---------------------- Faster --------------------------

$sweetsThief = Closure::bind(
    static function (Kitchen $kitchen) {
        return $kitchen->yummy;
    },
    null,
    "Kitchen"
);

Use Closure binding

Conclusions

PHP 7+ itself is very fast

PHP 8 + JIT will be even faster

Usually I/O is the bottleneck

Measure!

Sometimes you just don't have to be fast

Thanks!

Sources

Optimizing the Performance of PHP Applications

By Máté Kocsis

Optimizing the Performance of PHP Applications

  • 734