Skip to content

Commit bc07d4d

Browse files
Fixed benchmark
1 parent a81d1a7 commit bc07d4d

3 files changed

Lines changed: 101 additions & 53 deletions

File tree

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,16 @@ Because of that, the benchmark below uses a generated **N-Triples** dataset, not
431431

432432
The measurements below were taken with PHP 8.3.6 and Node.js 25.9.0 using `php perf/compare-hardf-n3js.php`. EasyRDF and ARC2 were measured with CLI opcache enabled. Hardf was measured both with and without CLI opcache enabled. N3.js is included as a reference implementation on a very fast runtime, so it is expected to win on absolute throughput.
433433

434+
```bash
435+
# Run the default comparison (Hardf vs N3.js)
436+
php perf/compare-hardf-n3js.php
437+
438+
# Also include EasyRDF and ARC2
439+
php perf/compare-hardf-n3js.php --all
440+
```
441+
442+
The script generates synthetic N-Triples datasets at `/tmp/hardf-perf/` and reports parse time and memory for each framework.
443+
434444
That expectation does show up in the results, but the interesting part is the size of the gap: Hardf remains within a single-digit factor of N3.js across the whole range and scales roughly linearly from $10^5$ to $10^7$ triples. CLI opcache helps Hardf modestly on runtime and significantly on reported PHP memory. EasyRDF and ARC2 fall further behind as the data grows.
435445

436446
We report on the findings for increasing number of triples.

perf/compare-hardf-n3js.php

Lines changed: 90 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,39 @@
66

77
function formatUsage(): void
88
{
9-
echo "Usage: compare-hardf-n3js.php [triple-count ...]\n";
9+
echo "Usage: compare-hardf-n3js.php [--all] [triple-count ...]\n";
10+
echo " --all Also benchmark EasyRDF and ARC2 (slower)\n";
1011
echo "Example: php perf/compare-hardf-n3js.php 100000 1000000 10000000\n";
12+
echo "Example: php perf/compare-hardf-n3js.php --all 100000\n";
1113
}
1214

13-
function parseSizes(array $argv): array
15+
function parseArgs(array $argv): array
1416
{
15-
if (1 === count($argv)) {
16-
return DEFAULT_SIZES;
17-
}
18-
1917
$sizes = [];
20-
foreach (array_slice($argv, 1) as $rawSize) {
21-
if (!preg_match('/^\d+$/', $rawSize)) {
18+
$all = false;
19+
20+
foreach (array_slice($argv, 1) as $arg) {
21+
if ('--all' === $arg) {
22+
$all = true;
23+
continue;
24+
}
25+
if (!preg_match('/^\d+$/', $arg)) {
2226
formatUsage();
2327
throw new InvalidArgumentException('Triple counts must be positive integers.');
2428
}
25-
$size = (int) $rawSize;
29+
$size = (int) $arg;
2630
if ($size <= 0) {
2731
formatUsage();
2832
throw new InvalidArgumentException('Triple counts must be positive integers.');
2933
}
3034
$sizes[] = $size;
3135
}
3236

33-
return $sizes;
37+
if (empty($sizes)) {
38+
$sizes = DEFAULT_SIZES;
39+
}
40+
41+
return ['sizes' => $sizes, 'all' => $all];
3442
}
3543

3644
function ensureNodeAvailable(): void
@@ -81,34 +89,54 @@ function parseBenchmarkOutput(string $output): array
8189
];
8290
}
8391

84-
function printResults(array $results): void
92+
function printResults(array $results, bool $all): void
8593
{
86-
echo "\n| triples | Hardf no opcache (ms) | Hardf no opcache (MB) | Hardf no opcache vs N3.js | Hardf opcache (ms) | Hardf opcache (MB) | Hardf opcache vs N3.js | EasyRDF opcache (ms) | EasyRDF opcache (MB) | EasyRDF vs N3.js | ARC2 opcache (ms) | ARC2 opcache (MB) | ARC2 vs N3.js | N3.js (ms) | N3.js (MB) |\n";
87-
echo "|--------:|----------------------:|----------------------:|--------------------------:|-------------------:|-------------------:|-----------------------:|---------------------:|---------------------:|-----------------:|------------------:|------------------:|---------------:|----------:|-----------:|\n";
94+
if ($all) {
95+
echo "\n| triples | Hardf no opcache (ms) | Hardf no opcache (MB) | Hardf no opcache vs N3.js | Hardf opcache (ms) | Hardf opcache (MB) | Hardf opcache vs N3.js | EasyRDF opcache (ms) | EasyRDF opcache (MB) | EasyRDF vs N3.js | ARC2 opcache (ms) | ARC2 opcache (MB) | ARC2 vs N3.js | N3.js (ms) | N3.js (MB) |\n";
96+
echo "|--------:|----------------------:|----------------------:|--------------------------:|-------------------:|-------------------:|-----------------------:|---------------------:|---------------------:|-----------------:|------------------:|------------------:|---------------:|----------:|-----------:|\n";
97+
} else {
98+
echo "\n| triples | Hardf no opcache (ms) | Hardf no opcache (MB) | Hardf no opcache vs N3.js | Hardf opcache (ms) | Hardf opcache (MB) | Hardf opcache vs N3.js | N3.js (ms) | N3.js (MB) |\n";
99+
echo "|--------:|----------------------:|----------------------:|--------------------------:|-------------------:|-------------------:|-----------------------:|----------:|-----------:|\n";
100+
}
88101

89102
foreach ($results as $row) {
90103
$hardfNoOpcacheSlower = slowerThanN3js($row['hardfNoOpcache']['seconds'], $row['n3js']['seconds']);
91104
$hardfOpcacheSlower = slowerThanN3js($row['hardfOpcache']['seconds'], $row['n3js']['seconds']);
92-
$easyRdfSlower = slowerThanN3js($row['easyRdfOpcache']['seconds'], $row['n3js']['seconds']);
93-
$arc2Slower = slowerThanN3js($row['arc2Opcache']['seconds'], $row['n3js']['seconds']);
94-
printf(
95-
"| %s | %.0f | %.3f | %.2fx | %.0f | %.3f | %.2fx | %.0f | %.3f | %.2fx | %.0f | %.3f | %.2fx | %.0f | %.3f |\n",
96-
number_format($row['triples']),
97-
$row['hardfNoOpcache']['seconds'] * 1000,
98-
$row['hardfNoOpcache']['memoryMb'],
99-
$hardfNoOpcacheSlower,
100-
$row['hardfOpcache']['seconds'] * 1000,
101-
$row['hardfOpcache']['memoryMb'],
102-
$hardfOpcacheSlower,
103-
$row['easyRdfOpcache']['seconds'] * 1000,
104-
$row['easyRdfOpcache']['memoryMb'],
105-
$easyRdfSlower,
106-
$row['arc2Opcache']['seconds'] * 1000,
107-
$row['arc2Opcache']['memoryMb'],
108-
$arc2Slower,
109-
$row['n3js']['seconds'] * 1000,
110-
$row['n3js']['memoryMb']
111-
);
105+
if ($all) {
106+
$easyRdfSlower = slowerThanN3js($row['easyRdfOpcache']['seconds'], $row['n3js']['seconds']);
107+
$arc2Slower = slowerThanN3js($row['arc2Opcache']['seconds'], $row['n3js']['seconds']);
108+
printf(
109+
"| %s | %.0f | %.3f | %.2fx | %.0f | %.3f | %.2fx | %.0f | %.3f | %.2fx | %.0f | %.3f | %.2fx | %.0f | %.3f |\n",
110+
number_format($row['triples']),
111+
$row['hardfNoOpcache']['seconds'] * 1000,
112+
$row['hardfNoOpcache']['memoryMb'],
113+
$hardfNoOpcacheSlower,
114+
$row['hardfOpcache']['seconds'] * 1000,
115+
$row['hardfOpcache']['memoryMb'],
116+
$hardfOpcacheSlower,
117+
$row['easyRdfOpcache']['seconds'] * 1000,
118+
$row['easyRdfOpcache']['memoryMb'],
119+
$easyRdfSlower,
120+
$row['arc2Opcache']['seconds'] * 1000,
121+
$row['arc2Opcache']['memoryMb'],
122+
$arc2Slower,
123+
$row['n3js']['seconds'] * 1000,
124+
$row['n3js']['memoryMb']
125+
);
126+
} else {
127+
printf(
128+
"| %s | %.0f | %.3f | %.2fx | %.0f | %.3f | %.2fx | %.0f | %.3f |\n",
129+
number_format($row['triples']),
130+
$row['hardfNoOpcache']['seconds'] * 1000,
131+
$row['hardfNoOpcache']['memoryMb'],
132+
$hardfNoOpcacheSlower,
133+
$row['hardfOpcache']['seconds'] * 1000,
134+
$row['hardfOpcache']['memoryMb'],
135+
$hardfOpcacheSlower,
136+
$row['n3js']['seconds'] * 1000,
137+
$row['n3js']['memoryMb']
138+
);
139+
}
112140
}
113141
}
114142

@@ -120,7 +148,9 @@ function printResults(array $results): void
120148
$generatedDir = rtrim(sys_get_temp_dir(), \DIRECTORY_SEPARATOR).\DIRECTORY_SEPARATOR.'hardf-perf';
121149

122150
try {
123-
$sizes = parseSizes($argv);
151+
$args = parseArgs($argv);
152+
$sizes = $args['sizes'];
153+
$all = $args['all'];
124154
ensureNodeAvailable();
125155
} catch (Throwable $error) {
126156
echo $error->getMessage()."\n";
@@ -162,21 +192,29 @@ function printResults(array $results): void
162192
escapeshellarg($filename),
163193
]));
164194

165-
$easyRdfOutput = runCommand(implode(' ', [
166-
escapeshellarg(\PHP_BINARY),
167-
'-d',
168-
escapeshellarg('opcache.enable_cli=1'),
169-
escapeshellarg($easyRdfBench),
170-
escapeshellarg($filename),
171-
]));
172-
173-
$arc2Output = runCommand(implode(' ', [
174-
escapeshellarg(\PHP_BINARY),
175-
'-d',
176-
escapeshellarg('opcache.enable_cli=1'),
177-
escapeshellarg($arc2Bench),
178-
escapeshellarg($filename),
179-
]));
195+
$easyRdfOpcache = null;
196+
$arc2Opcache = null;
197+
198+
if ($all) {
199+
$easyRdfOutput = runCommand(implode(' ', [
200+
escapeshellarg(\PHP_BINARY),
201+
'-d',
202+
escapeshellarg('opcache.enable_cli=1'),
203+
escapeshellarg($easyRdfBench),
204+
escapeshellarg($filename),
205+
]));
206+
207+
$arc2Output = runCommand(implode(' ', [
208+
escapeshellarg(\PHP_BINARY),
209+
'-d',
210+
escapeshellarg('opcache.enable_cli=1'),
211+
escapeshellarg($arc2Bench),
212+
escapeshellarg($filename),
213+
]));
214+
215+
$easyRdfOpcache = parseBenchmarkOutput($easyRdfOutput);
216+
$arc2Opcache = parseBenchmarkOutput($arc2Output);
217+
}
180218

181219
$n3Output = runCommand(implode(' ', [
182220
'node',
@@ -188,8 +226,8 @@ function printResults(array $results): void
188226
'triples' => $size,
189227
'hardfNoOpcache' => parseBenchmarkOutput($hardfOutput),
190228
'hardfOpcache' => parseBenchmarkOutput($hardfOpcacheOutput),
191-
'easyRdfOpcache' => parseBenchmarkOutput($easyRdfOutput),
192-
'arc2Opcache' => parseBenchmarkOutput($arc2Output),
229+
'easyRdfOpcache' => $easyRdfOpcache,
230+
'arc2Opcache' => $arc2Opcache,
193231
'n3js' => parseBenchmarkOutput($n3Output),
194232
];
195233
} catch (Throwable $error) {
@@ -202,4 +240,4 @@ function printResults(array $results): void
202240
}
203241
}
204242

205-
printResults($results);
243+
printResults($results, $all);

src/TriGWriter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,7 @@ public function end(): ?string
663663
$this->subject = null;
664664
}
665665
if (null !== $this->readCallback) {
666-
\call_user_func($this->readCallback, $this->string);
666+
\call_user_func($this->readCallback, $this->string ?? '');
667667
}
668668

669669
// Disallow further writing

0 commit comments

Comments
 (0)