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