Skip to content

Commit 2ab641c

Browse files
committed
docs: performance page measured on the dedicated server, PHP 8.5
- list page is about 0.01 ms per 25 rows on current PHP, not 0.002 ms; the old figure came from a laptop on PHP 8.1, where a slower htmlspecialchars() hid more of the construction cost - detail page is nearly 2x faster than plain arrays; memory is under 300 bytes per row - names the server and PHP version, and says why the gap narrows on older PHP - benchmark command drops the -n form, which fails on builds that load opcache from php.ini
1 parent 97cae71 commit 2ab641c

2 files changed

Lines changed: 33 additions & 29 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ Full guides and references ([browse on GitHub](https://github.com/interactivetoo
6464
- **Lookup**
6565
- [Method Reference](docs/method-reference.md) - every method, grouped by what it returns
6666
- [Troubleshooting](docs/troubleshooting.md) - common error messages and gotchas, with fixes
67-
- [Performance](docs/performance.md) - what SmartArray costs vs plain arrays: about 0.002 ms per 25-row page and 300 bytes per row
67+
- [Performance](docs/performance.md) - what SmartArray costs vs plain arrays: about 0.01 ms per 25-row page and under 300 bytes per row
6868
- [AI Reference](docs/ai-reference.md) - the complete API in one dense file, written for AI coding assistants
6969

7070
## You're Never Locked In

docs/performance.md

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# Performance: What SmartArray Costs vs Plain Arrays
22

33
At worst, wrapping a 25-row query
4-
result and rendering it costs about 2 microseconds (0.002 ms) more than the
4+
result and rendering it costs about 10 microseconds (0.00001 s) more than the
55
same page written by hand with plain arrays and manual HTML encoding; at best
6-
it comes out ahead, because SmartString often encodes faster than PHP's
7-
built-in encoder (2.5x on the long-text detail page below). Memory
8-
overhead is about 300 bytes per row no matter how large the fields are.
6+
it comes out ahead, because SmartString encodes long text faster than PHP's
7+
built-in encoder (nearly 2x on the detail page below). Memory overhead is
8+
under 300 bytes per row no matter how large the fields are.
99

1010
All times on this page are in milliseconds (ms), thousandths of a second.
1111
For scale: response-time research puts the threshold where people start to
@@ -34,9 +34,9 @@ versions are verified to produce byte-identical HTML before timing:
3434

3535
| Scenario | Plain array | SmartArray | Difference |
3636
|------------------------------------------------|-------------|------------|--------------------|
37-
| List page (25 rows, encoded title+summary) | 0.0271 ms | 0.0288 ms | +0.0017 ms (1.06x) |
38-
| Detail page (1 row, encoded 5KB body) | 0.0120 ms | 0.0047 ms | **2.5x faster** |
39-
| Raw loop (plain SmartArray, create + 50 reads) | 0.0004 ms | 0.0070 ms | +0.0066 ms |
37+
| List page (25 rows, encoded title+summary) | 0.0213 ms | 0.0309 ms | +0.0096 ms (1.45x) |
38+
| Detail page (1 row, encoded 5KB body) | 0.0094 ms | 0.0050 ms | **nearly 2x faster** |
39+
| Raw loop (plain SmartArray, create + 50 reads) | 0.0006 ms | 0.0077 ms | +0.0072 ms |
4040

4141
SmartArray times include constructing the object from the plain records
4242
array, the same work the database layer does when it returns results. The
@@ -45,18 +45,19 @@ output or encoding at all: it uses plain `SmartArray` (no HTML mode),
4545
where field reads return plain strings and no SmartString objects are
4646
created, so data-processing code skips the encoding layer entirely.
4747

48-
**Encoding is not where the time goes.** Render the same pages with no
48+
**Construction is where the time goes.** Render the same pages with no
4949
encoding on either side (plain SmartArray against plain arrays echoed
50-
raw) and the list page still measures +0.0066 ms: the overhead is
51-
construction either way, and SmartString encodes as fast as or faster
52-
than hand-encoding, so HTML mode adds nothing on net.
50+
raw) and the list page still measures +0.007 ms. HTML mode adds about
51+
0.003 ms on top for the 50 SmartString objects, and on longer fields
52+
SmartString's faster encoding turns that around, which is why the detail
53+
page wins.
5354

5455
To put the list-page row in perspective: to lose a single millisecond on
5556
one page load, your code would have to build and render that 25-row list
56-
about 600 times. Same math for the raw-loop row: wrapping one 25-row query
57-
result in a SmartArray and looping over it costs 0.0066 ms more than the
58-
plain array, so a single request would have to create 151 separate
59-
SmartArrays, 25 rows each, and loop over all of them - 3,775 rows - before
57+
about 100 times. Same math for the raw-loop row: wrapping one 25-row query
58+
result in a SmartArray and looping over it costs 0.0072 ms more than the
59+
plain array, so a single request would have to create about 140 separate
60+
SmartArrays, 25 rows each, and loop over all of them - 3,500 rows - before
6061
the total penalty reached one millisecond.
6162

6263
**Why the detail page is faster.** SmartString checks whether text is
@@ -71,14 +72,14 @@ for the encoding measurements across platforms.
7172

7273
| Data | Payload size | SmartArray adds | Per record |
7374
|--------------------|--------------|-----------------|------------|
74-
| 25 news records | ~133 KB | +7.8 KB | ~320 bytes |
75-
| 1,000 news records | ~5.2 MB | +294 KB | ~301 bytes |
75+
| 25 news records | ~133 KB | +6.4 KB | ~260 bytes |
76+
| 1,000 news records | ~5.2 MB | +247 KB | ~250 bytes |
7677

7778
Field values are never copied: PHP strings are reference-counted, so a 5KB
7879
content field is shared between the plain array and the SmartArray that
7980
wraps it. The per-record overhead is the row object itself, so the bytes
8081
per row are the constant, not a percentage: on these ~5 KB news records it
81-
works out to 6%, on records with a 50 KB body it would be 0.6%, and on
82+
works out to 5%, on records with a 50 KB body it would be 0.5%, and on
8283
lean three-column rows it could exceed 100% - while staying the same few
8384
hundred bytes each time.
8485

@@ -98,12 +99,12 @@ nearly all of it; everything after is close to free:
9899

99100
| Operation (25 rows, plain SmartArray) | Cost |
100101
|---------------------------------------|-------------|
101-
| Construct from plain records array | 0.0045 ms |
102-
| Construct via `fromDatabaseRows()` | 0.0037 ms |
103-
| foreach over all rows | 0.0007 ms |
104-
| Read a field (`$row->title`) | 0.000046 ms |
105-
| `toArray()` on the record set | 0.0006 ms |
106-
| `toArray()` on one flat row | 0.000027 ms |
102+
| Construct from plain records array | 0.0043 ms |
103+
| Construct via `fromDatabaseRows()` | 0.0033 ms |
104+
| foreach over all rows | 0.0008 ms |
105+
| Read a field (`$row->title`) | 0.000058 ms |
106+
| `toArray()` on the record set | 0.0009 ms |
107+
| `toArray()` on one flat row | 0.000033 ms |
107108

108109
ZenDB constructs its result sets with `fromDatabaseRows()`, the faster
109110
construct row above: database rows are uniform (same columns in every row,
@@ -125,18 +126,21 @@ Two things follow from construction being the whole cost:
125126
- **It's eager, per row fetched.** Query 500 rows to show 10 and you pay
126127
for 500. LIMIT in the query beats any amount of avoiding SmartArray.
127128
- **Hot loops: unwrap once.** A report loop touching every field thousands
128-
of times can call `->toArray()` first (0.000027 ms on a flat row) and
129+
of times can call `->toArray()` first (0.000033 ms on a flat row) and
129130
loop the plain array.
130131

131132
## Reproducing the Numbers
132133

133134
Every number on this page comes from one script, which builds the test
134135
data, verifies both versions produce byte-identical HTML, and then times
135-
them. Local runs are direction checks; numbers move a few percent between
136-
runs and more between machines. Run with opcache on and xdebug off:
136+
them. The numbers above are from a dedicated Linux x64 server (Intel Xeon
137+
E-2386G) on PHP 8.5 with opcache on and JIT off. The list-page gap tracks
138+
how fast the platform's own `htmlspecialchars()` runs: on PHP 8.1, or on a
139+
laptop, the plain-array side is slower and the gap narrows. Run it on your
140+
own machine with opcache on and xdebug off:
137141

138142
```bash
139-
php -n -d zend_extension=opcache -d opcache.enable_cli=1 benchmarks/news-page.php
143+
php -d opcache.enable_cli=1 -d xdebug.mode=off benchmarks/news-page.php
140144
```
141145

142146
---

0 commit comments

Comments
 (0)