How fast is PL/php compared to the built-in procedural languages? These are the first published numbers for the modernized extension. Reproduce them any time with the committed suite:
PGPORT=5432 sh bench/run.shPostgreSQL 18, PHP 8.3 (embed, NTS), single client, pgbench -T 8, one
warm session, Ubuntu 24.04 container on x86-64. Higher is better; treat
±3% as noise.
| Benchmark | PL/php | PL/pgSQL | PL/Perl |
|---|---|---|---|
Scalar math ((a*3+b)%97) |
58,800 | 60,000 | 57,900 |
| String ops (reverse+upper+length) | 43,400 | 44,000 | 46,300 |
| SPI loop over 1,000 rows | 4,700 | 8,600 | 2,700 |
| 10 small SPI statements per call | 23,500 | 26,000 | 18,700 |
- Scalar and string work is call-overhead-bound. All three languages sit within a few percent of each other: the executor's function-call machinery dominates, not the interpreter. PL/php's text-based argument conversion is not a measurable factor at this scale.
- Row iteration is PL/pgSQL's home turf. Its
FOR ... IN SELECTloop iterates natively without crossing a language boundary per row. PL/php pays a C-to-PHP conversion per row (one output-function call and one zval per column) and is still 1.75x faster than PL/Perl, which materializes the entire result into Perl structures up front. - Repeated SPI statements carry a per-call subtransaction in both PL/php
and PL/Perl (that is what makes their errors catchable); PL/pgSQL's
EXECUTEdoes not. PL/php lands between the two, ~25% ahead of PL/Perl.
Interning the column-name hash keys once per result (instead of hashing them
per row) measured as a no-op: the row-loop cost lives in per-cell value
conversion, not key handling. The optimization was dropped rather than
carried as complexity without benefit. A future fast path worth exploring is
converting common scalar types (int/float/bool/text) from their binary Datum
form instead of through the type output functions; that requires threading
type metadata into spi_fetch_row's result handling.
- For pure computation, use whichever language reads best; the overhead differences are negligible.
- For tight loops over large results, prefer a set-based SQL statement (or
PL/pgSQL) when the logic allows; when you need PHP's expressiveness per
row,
spi_query/spi_fetchrowkeeps memory flat and PL/php's per-row cost is the best of the interpreted PLs measured here.