xref: /PHP-8.3/benchmark/benchmark.php (revision ee6f9e29)
1<?php
2
3require_once __DIR__ . '/shared.php';
4
5$storeResult = ($argv[1] ?? 'false') === 'true';
6$phpCgi = $argv[2] ?? dirname(PHP_BINARY) . '/php-cgi';
7if (!file_exists($phpCgi)) {
8    fwrite(STDERR, "php-cgi not found\n");
9    exit(1);
10}
11
12function main() {
13    global $storeResult;
14
15    $data = [];
16    if (false !== $branch = getenv('GITHUB_REF_NAME')) {
17        $data['branch'] = $branch;
18    }
19    $data['Zend/bench.php'] = runBench(false);
20    $data['Zend/bench.php JIT'] = runBench(true);
21    $data['Symfony Demo 2.2.3'] = runSymfonyDemo(false);
22    $data['Symfony Demo 2.2.3 JIT'] = runSymfonyDemo(true);
23    $data['Wordpress 6.2'] = runWordpress(false);
24    $data['Wordpress 6.2 JIT'] = runWordpress(true);
25    $result = json_encode($data, JSON_PRETTY_PRINT) . "\n";
26
27    fwrite(STDOUT, $result);
28
29    if ($storeResult) {
30        storeResult($result);
31    }
32}
33
34function storeResult(string $result) {
35    $repo = __DIR__ . '/repos/data';
36    cloneRepo($repo, 'git@github.com:php/benchmarking-data.git');
37
38    $commitHash = getPhpSrcCommitHash();
39    $dir = $repo . '/' . substr($commitHash, 0, 2) . '/' . $commitHash;
40    $summaryFile = $dir . '/summary.json';
41    if (!is_dir($dir)) {
42        mkdir($dir, 0755, true);
43    }
44    file_put_contents($summaryFile, $result);
45}
46
47function getPhpSrcCommitHash(): string {
48    $result = runCommand(['git', 'log', '--pretty=format:%H', '-n', '1'], dirname(__DIR__));
49    return $result->stdout;
50}
51
52function runBench(bool $jit): array {
53    return runValgrindPhpCgiCommand([dirname(__DIR__) . '/Zend/bench.php'], jit: $jit);
54}
55
56function runSymfonyDemo(bool $jit): array {
57    $dir = __DIR__ . '/repos/symfony-demo-2.2.3';
58    cloneRepo($dir, 'https://github.com/php/benchmarking-symfony-demo-2.2.3.git');
59    runPhpCommand([$dir . '/bin/console', 'cache:clear']);
60    runPhpCommand([$dir . '/bin/console', 'cache:warmup']);
61    return runValgrindPhpCgiCommand([$dir . '/public/index.php'], cwd: $dir, jit: $jit, warmup: 50, repeat: 50);
62}
63
64function runWordpress(bool $jit): array {
65    $dir = __DIR__ . '/repos/wordpress-6.2';
66    cloneRepo($dir, 'https://github.com/php/benchmarking-wordpress-6.2.git');
67
68    /* FIXME: It might be better to use a stable version of PHP for this command because we can't
69     * easily alter the phar file */
70    runPhpCommand([
71        '-d error_reporting=0',
72        'wp-cli.phar',
73        'core',
74        'install',
75        '--url=wordpress.local',
76        '--title="Wordpress"',
77        '--admin_user=wordpress',
78        '--admin_password=wordpress',
79        '--admin_email=benchmark@php.net',
80    ], $dir);
81
82    // Warmup
83    runPhpCommand([$dir . '/index.php'], $dir);
84    return runValgrindPhpCgiCommand([$dir . '/index.php'], cwd: $dir, jit: $jit, warmup: 50, repeat: 50);
85}
86
87function runPhpCommand(array $args, ?string $cwd = null): ProcessResult {
88    return runCommand([PHP_BINARY, ...$args], $cwd);
89}
90
91function runValgrindPhpCgiCommand(
92    array $args,
93    ?string $cwd = null,
94    bool $jit = false,
95    int $warmup = 0,
96    int $repeat = 1,
97): array {
98    global $phpCgi;
99    $process = runCommand([
100        'valgrind',
101        '--tool=callgrind',
102        '--dump-instr=yes',
103        '--callgrind-out-file=/dev/null',
104        '--',
105        $phpCgi,
106        '-T' . ($warmup ? $warmup . ',' : '') . $repeat,
107        '-d max_execution_time=0',
108        '-d opcache.enable=1',
109        '-d opcache.jit_buffer_size=' . ($jit ? '128M' : '0'),
110        '-d opcache.validate_timestamps=0',
111        ...$args,
112    ]);
113    $instructions = extractInstructionsFromValgrindOutput($process->stderr);
114    if ($repeat > 1) {
115        $instructions = gmp_strval(gmp_div_q($instructions, $repeat));
116    }
117    return ['instructions' => $instructions];
118}
119
120function extractInstructionsFromValgrindOutput(string $output): string {
121    preg_match("(==[0-9]+== Events    : Ir\n==[0-9]+== Collected : (?<instructions>[0-9]+))", $output, $matches);
122    return $matches['instructions'] ?? throw new \Exception('Unexpected valgrind output');
123}
124
125main();
126