Skip to content

Commit ad42f61

Browse files
fulloclaude
andcommitted
Restructure examples: single file per example with 3 iterations
Replace before.php/after.php per-directory structure with a single file per example that accepts an iteration argument (1=naive, 2=optimized, 3=refined). This simulates a real profile→optimize→re-profile workflow. Structure: examples/ ├── 01-string-processing.php ← 3 iterations in match() ├── 02-database-simulation.php ← 3 iterations in match() ├── 03-json-api.php ← 3 iterations in match() ├── run-all.sh ← runs each with iteration 1,2,3 ├── results/ ← unified reports for all examples └── README.md Dashboard now shows the same script_filename with SCI progressively dropping across iterations — much clearer for analysis. Results: 01-string -30%, 02-database -98%, 03-json -16% Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d0432af commit ad42f61

26 files changed

Lines changed: 719 additions & 1043 deletions

examples/01-string-processing.php

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Example 01 — String Processing: progressive optimization.
7+
*
8+
* Simulates building an HTML report from 5,000 records.
9+
* Run 3 times with increasing iteration number to see SCI drop:
10+
*
11+
* php 01-string-processing.php 1 ← naive: .= in loop
12+
* php 01-string-processing.php 2 ← fix: array + implode
13+
* php 01-string-processing.php 3 ← refined: sprintf + single-pass stats
14+
*
15+
* @author fullo <https://github.com/fullo>
16+
* @license MIT
17+
* @version 1.0
18+
*/
19+
20+
$iteration = (int) ($argv[1] ?? 1);
21+
echo "=== String Processing — iteration {$iteration}/3 ===\n";
22+
23+
// ── Generate 5,000 user records (same seed for all iterations) ──
24+
mt_srand(42);
25+
$users = [];
26+
for ($i = 0; $i < 5000; $i++) {
27+
$users[] = [
28+
'id' => $i + 1,
29+
'name' => 'User ' . str_pad((string) ($i + 1), 4, '0', STR_PAD_LEFT),
30+
'email' => 'user' . ($i + 1) . '@example.com',
31+
'score' => mt_rand(0, 10000) / 100,
32+
'active' => $i % 7 !== 0,
33+
];
34+
}
35+
36+
$header = '<!DOCTYPE html><html><head><title>User Report</title>'
37+
. '<style>table{border-collapse:collapse}td,th{border:1px solid #ccc;padding:4px 8px}'
38+
. 'tr:nth-child(even){background:#f9f9f9}.inactive{color:#999}</style></head><body>'
39+
. '<h1>User Report</h1><p>Generated: ' . date('Y-m-d H:i:s') . '</p>'
40+
. '<table><thead><tr><th>ID</th><th>Name</th><th>Email</th><th>Score</th><th>Status</th></tr></thead><tbody>';
41+
42+
$footer = '</tbody></table>';
43+
44+
match ($iteration) {
45+
// ── Iteration 1: Naive — string concatenation in a loop ──
46+
// Each .= copies the entire $html string (O(n²) memory operations).
47+
1 => (function () use ($users, $header, $footer): string {
48+
$html = $header;
49+
50+
foreach ($users as $user) {
51+
$class = $user['active'] ? '' : ' class="inactive"';
52+
$status = $user['active'] ? 'Active' : 'Inactive';
53+
$html .= '<tr' . $class . '>';
54+
$html .= '<td>' . $user['id'] . '</td>';
55+
$html .= '<td>' . htmlspecialchars($user['name']) . '</td>';
56+
$html .= '<td>' . htmlspecialchars($user['email']) . '</td>';
57+
$html .= '<td>' . number_format($user['score'], 2) . '</td>';
58+
$html .= '<td>' . $status . '</td>';
59+
$html .= '</tr>';
60+
}
61+
62+
$html .= $footer;
63+
64+
// Summary: second loop over all users
65+
$active = 0;
66+
$total = 0.0;
67+
foreach ($users as $user) {
68+
if ($user['active']) {
69+
$active++;
70+
}
71+
$total += $user['score'];
72+
}
73+
$html .= '<p>Active: ' . $active . '/' . count($users) . '</p>';
74+
$html .= '<p>Avg score: ' . number_format($total / count($users), 2) . '</p>';
75+
$html .= '</body></html>';
76+
77+
echo 'Output: ' . strlen($html) . " bytes | Active: {$active}\n";
78+
return $html;
79+
})(),
80+
81+
// ── Iteration 2: Fix — array + implode, single allocation ──
82+
// Each $parts[] = '...' is O(1). implode() does one allocation at the end.
83+
2 => (function () use ($users, $header, $footer): string {
84+
$parts = [$header];
85+
86+
foreach ($users as $user) {
87+
$class = $user['active'] ? '' : ' class="inactive"';
88+
$status = $user['active'] ? 'Active' : 'Inactive';
89+
$parts[] = '<tr' . $class . '>'
90+
. '<td>' . $user['id'] . '</td>'
91+
. '<td>' . htmlspecialchars($user['name']) . '</td>'
92+
. '<td>' . htmlspecialchars($user['email']) . '</td>'
93+
. '<td>' . number_format($user['score'], 2) . '</td>'
94+
. '<td>' . $status . '</td>'
95+
. '</tr>';
96+
}
97+
98+
$parts[] = $footer;
99+
100+
// Summary: still a second loop
101+
$active = 0;
102+
$total = 0.0;
103+
foreach ($users as $user) {
104+
if ($user['active']) {
105+
$active++;
106+
}
107+
$total += $user['score'];
108+
}
109+
$parts[] = '<p>Active: ' . $active . '/' . count($users) . '</p>';
110+
$parts[] = '<p>Avg score: ' . number_format($total / count($users), 2) . '</p>';
111+
$parts[] = '</body></html>';
112+
113+
$html = implode('', $parts);
114+
echo 'Output: ' . strlen($html) . " bytes | Active: {$active}\n";
115+
return $html;
116+
})(),
117+
118+
// ── Iteration 3: Refined — sprintf rows + single-pass stats ──
119+
// All stats computed inline during the same loop. No second iteration.
120+
// sprintf for each row avoids intermediate concatenation.
121+
3 => (function () use ($users, $header, $footer): string {
122+
$parts = [$header];
123+
$active = 0;
124+
$total = 0.0;
125+
126+
foreach ($users as $user) {
127+
$parts[] = sprintf(
128+
'<tr%s><td>%d</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>',
129+
$user['active'] ? '' : ' class="inactive"',
130+
$user['id'],
131+
htmlspecialchars($user['name']),
132+
htmlspecialchars($user['email']),
133+
number_format($user['score'], 2),
134+
$user['active'] ? 'Active' : 'Inactive',
135+
);
136+
137+
if ($user['active']) {
138+
$active++;
139+
}
140+
$total += $user['score'];
141+
}
142+
143+
$parts[] = $footer;
144+
$parts[] = '<p>Active: ' . $active . '/' . count($users) . '</p>';
145+
$parts[] = '<p>Avg score: ' . number_format($total / count($users), 2) . '</p>';
146+
$parts[] = '</body></html>';
147+
148+
$html = implode('', $parts);
149+
echo 'Output: ' . strlen($html) . " bytes | Active: {$active}\n";
150+
return $html;
151+
})(),
152+
153+
default => throw new InvalidArgumentException("Usage: php 01-string-processing.php [1|2|3]"),
154+
};

examples/01-string-processing/after.php

Lines changed: 0 additions & 78 deletions
This file was deleted.

examples/01-string-processing/before.php

Lines changed: 0 additions & 76 deletions
This file was deleted.

0 commit comments

Comments
 (0)