LGV_TZ_Lookup 1.4.2
Time zone lookup for PHP applications and HTTP clients
Loading...
Searching...
No Matches
benchmark.php
Go to the documentation of this file.
1<?php
2
3declare(strict_types=1);
4
5// php -d memory_limit=1G tools/benchmark.php --fixture=/tmp/tz.sqlite --build
6// php -d memory_limit=1G tools/benchmark.php --fixture=/tmp/tz.sqlite --runs=3
7// php tools/benchmark.php --mysql=lgv_tz_benchmark_local --runs=3 --random=2000 --reconnect
8// php tools/benchmark.php --pgsql=lgv_tz_benchmark_local --runs=3 --random=2000
9$options = getopt('', ['fixture:', 'mysql:', 'pgsql:', 'source:', 'build', 'runs:', 'random:', 'results:', 'expect:', 'reconnect']);
10if (count(array_intersect(['fixture', 'mysql', 'pgsql'], array_keys($options))) !== 1) {
11 fwrite(STDERR, "Select one backend: --fixture=/path/to/isolated.sqlite, --mysql=lgv_tz_benchmark_NAME, or --pgsql=lgv_tz_benchmark_NAME\n");
12 exit(2);
13}
14$source = $options['source'] ?? dirname(__DIR__).'/src';
15require_once $source.'/Sources/LGV_TZ_Lookup_Query.class.php';
16require_once dirname(__DIR__).'/tests/support/SQLiteDatabase.php';
17
18if (isset($options['fixture']) && !isset($options['build']) && !is_file($options['fixture'])) {
19 fwrite(STDERR, "Build the isolated fixture first with --build.\n");
20 exit(2);
21}
22$createDatabase = static function() use ($options) {
23 if (isset($options['mysql']) || isset($options['pgsql'])) {
24 $driver = isset($options['pgsql']) ? 'pgsql' : 'mysql';
25 if (!preg_match('/^lgv_tz_benchmark_[a-zA-Z0-9_]+$/D', $options[$driver])) {
26 throw new InvalidArgumentException('Use a separate database named lgv_tz_benchmark_NAME.');
27 }
29 getenv('LGV_TZ_BENCH_USER') ?: ($driver === 'pgsql' ? (getenv('USER') ?: get_current_user()) : 'root'),
30 getenv('LGV_TZ_BENCH_PASSWORD') ?: '', $driver, getenv('LGV_TZ_BENCH_HOST') ?: 'localhost',
31 (int)(getenv('LGV_TZ_BENCH_PORT') ?: ($driver === 'pgsql' ? 5432 : 3306)));
32 }
33 return new SQLiteDatabase($options['fixture']);
34};
36if (isset($options['build'])) {
37 require_once $source.'/Sources/LGV_TZ_Lookup_Loader.class.php';
38 $stream = fopen($source.'/combined-with-oceans.json', 'rb');
39 if (!$stream) {
40 throw new RuntimeException('Boundary file unavailable.');
41 }
42 $start = hrtime(true);
43 $listener = new LGV_TZ_Lookup_Loader($database);
44 // One local transaction keeps disk commits out of the PHP loading comparison.
45 if ($database instanceof SQLiteDatabase) {
46 $database->pdo_instance->connection->beginTransaction();
47 }
48 try {
49 $parser = new JsonStreamingParser\Parser($stream, $listener);
50 $parser->parse();
51 if ($database instanceof SQLiteDatabase) {
52 $database->pdo_instance->connection->commit();
53 }
54 } finally {
55 fclose($stream);
56 }
57 echo json_encode([
58 'mode' => 'load', 'seconds' => (hrtime(true) - $start) / 1e9,
59 'peak_php_mib' => memory_get_peak_usage() / 1048576,
60 'polygons' => $database->pdo_instance->preparedStatement('SELECT COUNT(*) AS count FROM timezones', [], true)[0]['count'],
61 'polygon_bytes' => $database->pdo_instance->preparedStatement('SELECT SUM(LENGTH(polygon)) AS bytes FROM timezones', [], true)[0]['bytes'],
62 ], JSON_PRETTY_PRINT)."\n";
63 exit;
64}
65
66require dirname(__DIR__).'/src/TestLocations.php';
68mt_srand(20261007);
69for ($i = 0; $i < (int)($options['random'] ?? 0); ++$i) {
70 $cases[] = ['title' => 'Random '.$i, 'params' => [
71 'lng' => mt_rand(-180000000, 180000000) / 1e6,
72 'lat' => mt_rand(-90000000, 90000000) / 1e6,
73 ]];
74}
76$expectedResults = isset($options['expect']) ? json_decode(file_get_contents($options['expect']), true, 512, JSON_THROW_ON_ERROR) : null;
77$times = [];
84$start = hrtime(true);
85for ($run = 0; $run < max(1, (int)($options['runs'] ?? 3)); ++$run) {
86 foreach ($cases as $caseIndex => $case) {
87 $longitude = $case['params']['lng'];
88 $latitude = $case['params']['lat'];
89 while ($longitude < -180) { $longitude += 360; }
90 while ($longitude > 180) { $longitude -= 360; }
91 while ($latitude < -90) { $latitude += 180; }
92 while ($latitude > 90) { $latitude -= 180; }
93 if (function_exists('memory_reset_peak_usage')) { memory_reset_peak_usage(); }
94 $memory = memory_get_usage();
95 $caseStart = hrtime(true);
96 if (isset($options['reconnect'])) {
99 }
100 $result = $lookup->get_tz($longitude, $latitude);
101 $elapsed = (hrtime(true) - $caseStart) / 1e6;
102 $times[] = $elapsed;
103 $maxExtra = max($maxExtra, memory_get_peak_usage() - $memory);
104 $maxUsage = max($maxUsage, memory_get_peak_usage());
105 $maxReserved = max($maxReserved, memory_get_peak_usage(true));
106 if ($run === 0) {
107 $results[] = $result;
108 $slowest[] = ['title' => $case['title'], 'milliseconds' => $elapsed];
109 if (isset($case['result']) && $case['result'] !== $result) {
110 $failures[] = ['title' => $case['title'], 'expected' => $case['result'], 'actual' => $result];
111 }
112 } elseif ($results[$caseIndex] !== $result) {
113 throw new RuntimeException('Result changed between runs: '.$case['title']);
114 }
115 }
116}
117$seconds = (hrtime(true) - $start) / 1e9;
118sort($times);
119usort($slowest, static fn($a, $b) => $b['milliseconds'] <=> $a['milliseconds']);
120if (isset($options['results'])) {
121 file_put_contents($options['results'], json_encode($results, JSON_PRETTY_PRINT)."\n");
122}
123echo json_encode([
124 'mode' => 'query', 'backend' => isset($options['pgsql']) ? 'pgsql' : (isset($options['mysql']) ? 'mysql' : 'sqlite'),
125 'reconnect' => isset($options['reconnect']),
126 'cases' => count($cases), 'lookups' => count($times), 'seconds' => $seconds,
127 'mean_ms' => array_sum($times) / count($times), 'median_ms' => $times[(int)floor(count($times) * .5)],
128 'p95_ms' => $times[min(count($times) - 1, (int)floor(count($times) * .95))],
129 'max_ms' => max($times), 'max_extra_php_mib' => $maxExtra / 1048576,
130 'peak_php_mib' => $maxUsage / 1048576, 'peak_reserved_php_mib' => $maxReserved / 1048576,
131 'statements' => $database instanceof SQLiteDatabase ? $database->pdo_instance->statements : null,
132 'failures' => $failures, 'slowest' => array_slice($slowest, 0, 5),
133], JSON_PRETTY_PRINT)."\n";
134if (null !== $expectedResults && $expectedResults !== $results) {
135 fwrite(STDERR, "Results differ from the supplied baseline.\n");
136 exit(1);
137}
138exit(empty($failures) ? 0 : 1);
$test_locations_param_array
This file has a list of long/lat pairs, along with the expected results, when presented to the server...
$maxUsage
Definition benchmark.php:81
$results
Definition benchmark.php:78
$start
Definition benchmark.php:84
$expectedResults
Definition benchmark.php:76
$maxExtra
Definition benchmark.php:80
$maxReserved
Definition benchmark.php:82
if(isset($options['fixture']) &&!isset($options['build']) &&!is_file($options['fixture'])) $createDatabase
Definition benchmark.php:22
$times
Definition benchmark.php:77
$slowest
Definition benchmark.php:83
$failures
Definition benchmark.php:79
for($run=0; $run< max(1,(int)($options['runs'] ?? 3));++$run) $seconds
$options
Definition composer.php:6
$database
Definition composer.php:52
$lookup
Definition composer.php:54
$driver
Definition databases.php:38
$cases
Definition demo.php:82
$source
Definition demo.php:67