Lab / Open methodology
PHP report optimization: one-pass aggregation
200,000 orders summarized: 1.27 s → 8.49 ms. Synthetic lab; median of 10 runs. Open code and raw data.
Rescan for every customertoAggregate in one pass
PHP 8.4 LAB MEASUREMENT
Stop reading the same orders again and again.
Order totals by customer. Same PHP version, same data, same output; a different way to compute it.
Repeated reports consume less CPU. The task spends less time running while the team waits for its result.
Rescan for every customer
Read the order list again for each customer.
- CPU time
- 1.265 s
Aggregate in one pass
Read orders once and accumulate totals by customer key.
- CPU time
- 8.495 ms
Bars start at zero and share the same time scale across implementations.
Replace a full scan per customer with totals indexed by customer key. Business rules and integer-cent amounts remain identical.
All load points and measurements
| Implementation | Median duration | CPU time |
|---|---|---|
| Rescan for every customer | 2.93 ms | 2.916 ms |
| Aggregate in one pass | 0.04 ms | 0.041 ms |
| Implementation | Median duration | CPU time |
|---|---|---|
| Rescan for every customer | 8.362 ms | 8.363 ms |
| Aggregate in one pass | 0.114 ms | 0.114 ms |
| Implementation | Median duration | CPU time |
|---|---|---|
| Rescan for every customer | 28.426 ms | 28.429 ms |
| Aggregate in one pass | 0.371 ms | 0.373 ms |
| Implementation | Median duration | CPU time |
|---|---|---|
| Rescan for every customer | 83.354 ms | 83.352 ms |
| Aggregate in one pass | 0.898 ms | 0.902 ms |
| Implementation | Median duration | CPU time |
|---|---|---|
| Rescan for every customer | 190.504 ms | 190.479 ms |
| Aggregate in one pass | 2.059 ms | 2.065 ms |
| Implementation | Median duration | CPU time |
|---|---|---|
| Rescan for every customer | 436.716 ms | 436.251 ms |
| Aggregate in one pass | 4.175 ms | 4.183 ms |
| Implementation | Median duration | CPU time |
|---|---|---|
| Rescan for every customer | 1.265 s | 1.265 s |
| Aggregate in one pass | 8.487 ms | 8.495 ms |
CPU cost at your workload
Compare the recorded CPU time at your own monthly volume.
One batch run = 200,000 Order records.
CPU time reduced / month3.49 hours
Processing cost difference / month—Calculated after you enter a unit price.
Lab CPU time × your volume and unit price. This is not a hosting bill forecast; idle capacity, networking, storage, licences and migration are excluded.
Fix the query and tenant access together.
Normal order
BeforePassed
✓ AfterPassed
Missing order
BeforePassed
✓ AfterPassed
Apostrophe in order reference
BeforeQuery error
✓ AfterPassed
SQL input bypass
Before6 unauthorized rows
✓ AfterPassed
SQL input targeting another tenant
Before6 unauthorized rows
✓ AfterPassed
Direct cross-tenant reference
Before1 unauthorized rows
✓ AfterPassed
Try it yourself
Replay the recorded lab results side by side.
As the workload grows
Duration by workload
- Before
- After
Duration by workload (ms)
Show as a table
| Order records | Before | After |
|---|---|---|
| 1,000 | 2.93 ms | 0.04 ms |
| 3,000 | 8.36 ms | 0.11 ms |
| 10,000 | 28.43 ms | 0.37 ms |
| 30,000 | 83.35 ms | 0.9 ms |
| 60,000 | 190.5 ms | 2.06 ms |
| 100,000 | 436.72 ms | 4.18 ms |
| 200,000 | 1,265.12 ms | 8.49 ms |
How we measured
Environment, protocol and technical analysis
Environment and data
- Environment
- Apple M3; Docker Linux arm64, 2-CPU quota, 1 GiB memory. PHP 8.4.26 / SQLite 3.46.1.
- Dataset
- 200 customers; 1,000–200,000 deterministic orders. All result rows and cent totals match across implementations.
- Tools
- PHP 8.4.26 · SQLite 3.46.1 · hrtime / getrusage · OPcache / JIT off · native arm64 Docker
- Repetitions
- 7 loads × 5 implementations × 10 repeats, plus 70 warmup observations. Every sample retained; rotating arm order.
Stop reading the same orders again and again.
Order totals by customer. Same PHP version, same data, same output; a different way to compute it.
Identical PHP/SQLite versions, synthetic records and 200 customers. Two warmups then 10 runs per arm/size; every complete output matches an independent oracle. PHP lookup totals rebuilt each run. SQL timings are warm in-memory reads excluding loading/index creation. Index bytes and the single construction observation are separate. Primary 200,000-record point selected before measurement.
Inspect the measurement script ↗What changes for the business?
Repeated reports consume less CPU. The task spends less time running while the team waits for its result.
Change and scope
Replace a full scan per customer with totals indexed by customer key. Business rules and integer-cent amounts remain identical.
PHP 8.4.26 CLI; synthetic in-memory data, 200 customers. OPcache/JIT off. This measures a code improvement, not a PHP 7 → 8 speedup.
Bind values and enforce access
This separate synthetic order-lookup test is independent of the performance results. All six checks are published.
| Implementation | Passing checks | Remaining issue |
|---|---|---|
| Input concatenated into SQL | 2 / 6 | Apostrophe handling, SQL injection and cross-tenant access |
| Prepared statement only | 5 / 6 | A direct reference still reaches another tenant’s order |
| Prepared + tenant-scoped | 6 / 6 | No issue in these six checks |
Inspect every input and returned record. A PHP version upgrade does not replace these controls.
Where to start in your system
Identify the slow operation, data volume and peak workload. Establish its baseline, change the code and access pattern, then compare the same result.
What we did
Business rule and data
AIPrepared the synthetic workflow, tests and measurement automation.
VerificationPublished code and raw outputs are available for review; this is not a client project.
Implementation change
AIReplace a full scan per customer with totals indexed by customer key. Business rules and integer-cent amounts remain identical.
VerificationOutput equivalence was independently checked; every repetition is retained.