11# Performance: What SmartArray Costs vs Plain Arrays
22
33At 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
55same 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
1010All times on this page are in milliseconds (ms), thousandths of a second.
1111For 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
4141SmartArray times include constructing the object from the plain records
4242array, 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),
4545where field reads return plain strings and no SmartString objects are
4646created, 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
4949encoding 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
5455To put the list-page row in perspective: to lose a single millisecond on
5556one 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
6061the 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
7778Field values are never copied: PHP strings are reference-counted, so a 5KB
7879content field is shared between the plain array and the SmartArray that
7980wraps it. The per-record overhead is the row object itself, so the bytes
8081per 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
8283lean three-column rows it could exceed 100% - while staying the same few
8384hundred 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
108109ZenDB constructs its result sets with ` fromDatabaseRows() ` , the faster
109110construct 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
133134Every number on this page comes from one script, which builds the test
134135data, 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