Skip to content

Commit ae7f8f0

Browse files
committed
Compare parsing and compilation speed to LightnCandy
PHP Handlebars: - JIT on: Compiled 1000 times in 3.081 s | 3.081 ms/compile | 21.9 KB code - JIT off: Compiled 1000 times in 6.122 s | 6.122 ms/compile | 21.9 KB code LightnCandy: - JIT on: Compiled 1000 times in 5.135 s | 5.135 ms/compile | 34.6 KB code - JIT off: Compiled 1000 times in 6.444 s | 6.444 ms/compile | 34.6 KB code Parses and compiles Handlebars templates up to 40% faster than LightnCandy. Runtime code is over 35% smaller.
1 parent c20cdf9 commit ae7f8f0

5 files changed

Lines changed: 496 additions & 2 deletions

File tree

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
],
2020
"require": {
2121
"php": ">=8.2",
22-
"devtheorem/php-handlebars-parser": "^1.1.0"
22+
"devtheorem/php-handlebars-parser": "^1.1.0",
23+
"zordius/lightncandy": "^1.2"
2324
},
2425
"require-dev": {
2526
"friendsofphp/php-cs-fixer": "^3.94",

composer.lock

Lines changed: 57 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/benchmark.php

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
/**
4+
* Compiler benchmark script. Default iterations: 1000.
5+
*
6+
* Usage: php -d opcache.enable_cli=1 -d opcache.jit=tracing tests/benchmark.php
7+
*/
8+
9+
use DevTheorem\Handlebars\{Handlebars, Options};
10+
11+
require __DIR__ . '/../vendor/autoload.php';
12+
13+
$iterations = (int) ($argv[1] ?? 1000);
14+
$filename = __DIR__ . "/largeTemplate.hbs";
15+
16+
// A large, complex template exercising as many syntax features as possible.
17+
$template = file_get_contents($filename);
18+
if ($template === false) {
19+
exit("Failed to open $filename");
20+
}
21+
22+
$options = new Options(
23+
helpers: [
24+
't' => function (string $a) {
25+
return $a;
26+
},
27+
'formatDate' => function (mixed $value, string $format) {
28+
return date($format, strtotime($value));
29+
},
30+
'formatCurrency' => function (mixed $value, string $format) {
31+
return $format . ' ' . number_format($value, 2);
32+
},
33+
'replace' => function (string $subject, string $search, string $replace) {
34+
return str_replace($search, $replace, $subject);
35+
},
36+
'eq' => function (mixed $a, mixed $b) {
37+
if ($a === null || $b === null) {
38+
// in JS, null is not equal to blank string or false or zero
39+
return $a === $b;
40+
}
41+
42+
return $a == $b;
43+
},
44+
'and' => fn(mixed $a, mixed $b) => $a && $b,
45+
'not' => fn(mixed $a) => !$a,
46+
'gt' => fn(mixed $a, mixed $b) => $a > $b,
47+
],
48+
);
49+
50+
// Warm up: give the JIT a chance to compile hot paths before we measure.
51+
for ($i = 0; $i < 50; $i++) {
52+
Handlebars::precompile($template, $options);
53+
}
54+
55+
$start = hrtime(true);
56+
57+
for ($i = 0; $i < $iterations; $i++) {
58+
Handlebars::precompile($template, $options);
59+
}
60+
61+
$elapsed = (hrtime(true) - $start) / 1e9;
62+
$perParse = $elapsed / $iterations * 1000;
63+
$codeBytes = strlen(Handlebars::precompile($template, $options));
64+
65+
printf(
66+
"Compiled %d times in %.3f s | %.3f ms/compile | %.1f KB code\n",
67+
$iterations,
68+
$elapsed,
69+
$perParse,
70+
$codeBytes / 1024,
71+
);

0 commit comments

Comments
 (0)