Как тестировать производительность PHP

Чтобы протестировать PHP, можно воспользоваться небольшой программой, которую я специально разработал и выложил на https://github.com/anton-pribora/php-benchmark. Для этого в консоли нужно выполнить комманду:

wget -qO- https://raw.githubusercontent.com/anton-pribora/php-benchmark/master/get_and_start_multitest.sh | bash

Программа произведёт серию испытаний и выдаст результат в виде таблицы, где будет указана операция и результат. Чем больше результат, тем лучше.

Testing PHP 5.6.40 ... done
Testing PHP 7.1.33 ... done
Testing PHP 7.2.24 ... done
Testing PHP 7.3.11 ... done
Testing PHP 7.4.0 ... done
Common KVM processor   Units     PHP 5.6.40  PHP 7.1.33  PHP 7.2.24  PHP 7.3.11  PHP 7.4.0
Simple math            oper/sec  9,694,704   76,749,134  93,396,556  88,212,974  88,561,615
String concat          oper/sec  14,455,661  21,454,010  25,068,760  26,464,926  25,779,646
Array + array          oper/sec  641,446     907,981     969,094     1,192,167   1,110,056
Use array_merge        oper/sec  478,435     792,310     1,232,440   2,024,396   1,891,306
Create empty object    obj/sec   8,633,215   11,514,301  14,435,884  16,557,935  18,596,756
Foreach array          iter/sec  23,658,767  59,150,973  63,562,887  69,153,163  69,161,292
Use array_walk         iter/sec  7,621,713   12,974,978  13,745,403  14,070,210  14,051,297
Foreach array object   iter/sec  7,719,552   13,601,564  13,440,295  13,929,112  13,550,944
Foreach simple object  iter/sec  8,082,326   16,526,538  26,903,151  29,073,493  28,065,508
Call closure           call/sec  10,452,182  24,726,970  28,977,637  30,060,239  29,499,678
Call object function   call/sec  11,557,581  33,741,340  34,562,534  43,925,066  43,565,275
Call user function     call/sec  12,487,432  33,463,550  41,150,988  46,615,301  43,979,755
Function rand          call/sec  14,754,127  33,721,757  41,237,945  39,209,291  41,705,133
Function is_null       call/sec  10,307,678  48,748,330  54,757,062  74,051,775  76,142,248
Function empty         call/sec  38,389,411  46,019,203  50,974,064  59,100,247  70,081,321
Function isset         call/sec  37,551,313  51,627,412  65,115,665  70,121,536  79,073,355

Свой тест производительности

Также можно самостоятельно провести испытание. Для этого нужно сделать тестовый скрипт и придумать операцию для проверки.

Тестовый скрипт должен считать количество итераций в единицу времени. Таким образом рассчитывается скорость работы. Чем больше циклов успеет выполниться, тем лучше.

Вот пример тестирования работы с объектами (исходный код на pastebin):

$start   = microtime(true);
$cycles  = 0;
$elapsed = 0;
$result  = '';

do {
    $loops = 1000;
    $begin = microtime(true);
    do {
        // Тестируемая операция
        $result .= new A(array('foo' => 'bar'));
    } while (--$loops);
    $cycles  += 1000;
    $elapsed += microtime(true) - $begin;
} while (microtime(true) - $start < 1);

printf("%s: %.0f cycle/sec, %s bytes, %s bytes/cycle\n", PHP_VERSION, $cycles / $elapsed, strlen($result), strlen($result) / $cycles);

Объект класса создаётся и тут же добавляется в результат в виде строки. Общее время выполнения теста будет чуть больше одной секунды. За эту секунду будет выполняться вложенный цикл do while, в котором будет срабатывать тестируемая операция. Такая вложенность нужна, чтобы уменьшить количество вызовов функции microtime.

Код тестируемого класса:

class A
{
    private $foo;
    private $bar;
    private $baz;
    private $bee;

    public function __construct($array)
    {
        foreach($this as $prop => $value) {
            if ( isset($array[$prop]) ) {
                $this->{$prop} = $array[$prop];
            }
        }
    }

    public function __toString()
    {
        $result = array();

        foreach($this as $prop => $value) {
            $result[] = $prop .': '. $this->{$prop};
        }

        return join("\n", $result);
    }
}

Если запустить код, то выведется примерно следующее:

% php test.php 
5.4.45-0+deb7u2: 107945 cycle/sec, 2808000 bytes, 26 bytes/cycle

Сто семь тысяч циклов в секунду! Неплохой результат.

Всё познаётся в сравнении

На моём тестовом ноутбуке скомпилированы разные версии PHP. Я провёл тест на всех версиях:

% ls ./php-bin/php-* | xargs -I% sh -c '% test.php'
5.0.5: 111200 cycle/sec, 1344000 bytes, 12 bytes/cycle
5.1.6: 205900 cycle/sec, 2472000 bytes, 12 bytes/cycle
5.2.17: 96063 cycle/sec, 2522000 bytes, 26 bytes/cycle
5.3.29: 101803 cycle/sec, 2652000 bytes, 26 bytes/cycle
5.4.45: 110970 cycle/sec, 2886000 bytes, 26 bytes/cycle
5.5.33: 108943 cycle/sec, 2834000 bytes, 26 bytes/cycle
5.6.19: 116854 cycle/sec, 3042000 bytes, 26 bytes/cycle
7.0.4: 165556 cycle/sec, 4316000 bytes, 26 bytes/cycle
7.1.0-dev: 173207 cycle/sec, 4524000 bytes, 26 bytes/cycle

Из результатов видно, что начиная с версии 5.2 изменилась работа со строками, а самый производительный оказалась версия 5.1. Также видно, что после изменений в версии 5.2 производительность упала в два раза. Седьмые версии показывают увеличение производительности примерно в полтора раза по сравнению с 5.6.

График производительности разных версий PHP