Skip to content

perf: skip decode -> re-encode of subquery rows (binary DataRow -> binary array) - #2040

Open
jkaczman wants to merge 1 commit into
jk-subqueries-test-suite-assert-sqlfrom
jk-subquery-binary-conversion-subqueries
Open

jkaczman wants to merge 1 commit into
jk-subqueries-test-suite-assert-sqlfrom
jk-subquery-binary-conversion-subqueries

Conversation

@jkaczman

Copy link
Copy Markdown
Contributor

Previously, for a statement such as

SELECT * FROM orders WHERE user_id IN (SELECT id FROM users WHERE status = 'VIP')

Say that the subquery here returns the IDs 1 and 2. We fetched those rows in text format, then

  1. We decoded those rows into the Datum type; Datum::Bigint(1), Datum::Bigint(2)
  2. We encoded the Datums again (Array::encode) into the array {1,2}
  3. We sent the array to the shards as $1 of the rewritten statement SELECT * FROM orders WHERE user_id IN (SELECT u.c1 FROM unnest($1::pg_catalog._int8) u(c1))

Steps 1 and 2 here used to act as intermediate steps for a decode -> re-encode process that are now replaced with a binary -> binary conversion (data row binary -> bind parameter binary), as we don't need the decoded values elsewhere.

How? This forces all subqueries we execute to use the binary format (even if client requested text) to allow binary -> binary for massive speedup. This is fine, as the client never sees the rows of those subqueries; we only use them to rewrite the outer statement.

This gives us two advantages, (1) a performance increase, (2) being able to handle values that our decoding / encoding process doesn't support or has bugs for.

Measured to be roughly a 30% performance improvement of subquery handling.

@jkaczman
jkaczman added this pull request to stack #2041 November 15, 2026 00:14
@codecov

codecov Bot commented Nov 15, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.85057% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...end/client/query_engine/multi_step/subquery/mod.rs 85.71% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@github-actions

Copy link
Copy Markdown
Contributor

Metal bench (arm64) failed. 7 of 517 measures outside bounds.

suite throughput cpu/txn user cycles/txn user instr/txn kernel cycles/txn kernel instr/txn ctx/txn
pooler 19.34 (+1.1% ± 1.3%) 18,531 (+1.2% ± 0.7%) 31,553 (-0.0% ± 0.07%) 31,003 (+1.1% ± 2.2%) 51,051 (+0.0% ± 0.5%)
lb 19.71 (-1.0% ± 1.7%) 19,024 (-0.4% ± 0.9%) 32,475 (+0.0% ± 0.10%) 31,264 (-1.1% ± 2.8%) 52,136 (+0.2% ± 0.6%)
sharding-w2 31.69 (+0.2% ± 1.5%) 42,691 (+0.2% ± 1.4%) 94,868 (-0.0% ± 0.00%) 39,643 (+0.1% ± 2.4%) 53,701 (-0.1% ± 0.7%) 1.551 (-0.0% ± 0.1%)
sharding-w8 248,167 (+0.2% ± 1.4%) 28.73 (-0.2% ± 1.2%) 43,825 (-0.7% ± 1.5%) 50,429 (-0.0% ± 0.05%) 35,500 (+0.3% ± 1.1%) 42,381 (-0.0% ± 0.2%) 0.1721 (-0.1% ± 1.3%)
Counters per txn
suite frontend stall l1i refill backend stall
pooler 8,227 (+3.2% ± 2.1%) 2,723 (+1.3% ± 1.0%)
lb 8,479 (-0.7% ± 2.2%) 2,759 (-0.3% ± 1.1%)
sharding-w2 17,066 (+1.1% ± 3.2%) 4,055 (+0.5% ± 1.6%)
sharding-w8 19,376 (-0.8% ± 4.1%) 4,012 (-0.8% ± 1.9%)
benchmark measure result baseline change bound
full_extended_cached::having alloc_count 481 480 +0.21% ± 0.00% +0%
full_named_cached::having alloc_count 470 469 +0.21% ± 0.00% +0%
full_named_cached::order_limit_offset instructions 190,346 183,551 +3.70% ± 0.00% +1%
full_named_cached::select_all_shards instructions 149,421 147,785 +1.11% ± 0.00% +1%
full_simple::having alloc_count 471 469 +0.43% ± 0.00% +0%
full_simple::having alloc_bytes 53,140 52,400 +1.41% ± 0.00% +1%
full_simple::having_distinct_limit alloc_bytes 54,742 54,004 +1.37% ± 0.03% +1%

Microbench: 497 of 504 measures within bounds.

Hot code per txn
path base head change
pooler 114.4 KB 114.5 KB +0.1 KB
sharding-w2 167.0 KB 167.1 KB +0.1 KB

pooler:

crate base head change
pgdog::frontend::client::query_engine 19.9 KB 20.0 KB +0.1 KB
tokio 18.4 KB 18.5 KB +0.1 KB
pgdog::backend::pool 17.8 KB 17.4 KB -0.3 KB
aws-lc (TLS crypto) 10.3 KB 10.5 KB +0.2 KB
rustls (TLS) 8.4 KB 8.3 KB -0.1 KB
pgdog::frontend (other) 7.8 KB 8.1 KB +0.2 KB
pgdog::backend::server 7.8 KB 7.9 KB +0.1 KB
other 6.1 KB 5.7 KB -0.4 KB
everything else 18.1 KB 18.1 KB +0.0 KB
function head change
pgdog::frontend::client::Client::run::{{closure}} 5.6 KB +0.2 KB
pgdog::frontend::client::query_engine::query::<impl pgdog::frontend::client::query_engine: 5.2 KB +0.1 KB
pgdog::frontend::client::query_engine::QueryEngine::handle::{{closure}} 4.3 KB +0.1 KB
tokio::runtime::task::raw::poll 3.2 KB -0.1 KB
pgdog::frontend::client::query_engine::query::<impl pgdog::frontend::client::query_engine: 3.2 KB +0.0 KB

sharding-w2:

crate base head change
pgdog::frontend::router 29.2 KB 29.4 KB +0.2 KB
pgdog::frontend::client::query_engine 20.7 KB 20.8 KB +0.1 KB
tokio 18.4 KB 18.6 KB +0.2 KB
pgdog::backend::pool 18.3 KB 18.1 KB -0.2 KB
aws-lc (TLS crypto) 13.1 KB 12.6 KB -0.4 KB
other 10.0 KB 9.9 KB -0.1 KB
pgdog::frontend (other) 9.0 KB 8.9 KB -0.1 KB
rustls (TLS) 7.9 KB 8.2 KB +0.2 KB
everything else 40.3 KB 40.4 KB +0.1 KB
function head change
pgdog::frontend::client::Client::run::{{closure}} 6.4 KB +0.1 KB
pgdog::frontend::client::query_engine::query::<impl pgdog::frontend::client::query_engine: 5.1 KB -0.1 KB
pgdog::frontend::client::query_engine::QueryEngine::handle::{{closure}} 4.1 KB +0.2 KB
pgdog::frontend::router::parser::query::select::<impl pgdog::frontend::router::parser::que 3.2 KB +0.0 KB
pgdog::frontend::client::query_engine::query::<impl pgdog::frontend::client::query_engine: 3.2 KB +0.0 KB
lb flamegraph vs 3d93c4d

lb

Open interactive for hover, zoom and search.

lb removed

Open interactive.

microbench flamegraph vs 3d93c4d

microbench

Open interactive for hover, zoom and search.

microbench removed

Open interactive.

pooler flamegraph vs 3d93c4d

pooler

Open interactive for hover, zoom and search.

pooler removed

Open interactive.

sharding-w2 flamegraph vs 3d93c4d

sharding-w2

Open interactive for hover, zoom and search.

sharding-w2 removed

Open interactive.

sharding-w8 flamegraph vs 3d93c4d

sharding-w8

Open interactive for hover, zoom and search.

sharding-w8 removed

Open interactive.

Logs

lb, microbench, pooler, sharding-w2, sharding-w8, verdict.json

Compared 08fe1c7 with 3d93c4d on m8g.metal-24xl (spot).

@github-actions

Copy link
Copy Markdown
Contributor

Metal bench (x86) failed. 7 of 517 measures outside bounds.

suite throughput cpu/txn user cycles/txn user instr/txn kernel cycles/txn kernel instr/txn ctx/txn
pooler 25.75 (+0.0% ± 0.5%) 18,741 (-0.1% ± 0.1%) 32,946 (+0.0% ± 0.05%) 33,323 (+0.1% ± 0.9%) 36,510 (+0.1% ± 0.5%)
lb 26.09 (-0.0% ± 0.4%) 19,237 (+0.0% ± 0.1%) 33,925 (+0.0% ± 0.03%) 33,385 (-0.1% ± 0.6%) 37,281 (+0.1% ± 0.3%)
sharding-w2 41.57 (-0.1% ± 1.5%) 46,606 (+0.1% ± 0.4%) 98,565 (-0.0% ± 0.07%) 38,055 (-0.2% ± 2.9%) 40,154 (-0.0% ± 0.6%) 1.529 (-0.0% ± 0.6%)
sharding-w8 207,076 (-0.2% ± 0.6%) 32.88 (+0.1% ± 0.4%) 45,851 (+0.3% ± 0.3%) 52,875 (+0.0% ± 0.04%) 31,352 (-0.1% ± 0.6%) 32,504 (+0.0% ± 0.1%) 0.172 (-0.1% ± 2.9%)
Counters per txn
suite frontend stall l1i refill backend stall
pooler 3,374 (-0.5% ± 0.9%) 1,144 (-0.4% ± 0.9%) 38,941 (+0.1% ± 0.5%)
lb 3,488 (-0.4% ± 0.4%) 1,186 (+0.1% ± 0.8%) 39,765 (+0.4% ± 0.2%)
sharding-w2 5,976 (+0.7% ± 1.4%) 1,509 (+0.1% ± 0.4%) 114,718 (-0.0% ± 0.9%)
sharding-w8 3,489 (+0.4% ± 0.8%) 874 (+0.4% ± 1.2%) 176,974 (+0.5% ± 0.5%)
benchmark measure result baseline change bound
full_extended_cached::having alloc_count 481 480 +0.21% ± 0.00% +0%
full_named_cached::having alloc_count 470 469 +0.21% ± 0.00% +0%
full_named_cached::having_distinct_limit instructions 456,136 451,340 +1.06% ± 0.00% +1%
full_named_cached::order_limit_offset instructions 199,787 192,953 +3.54% ± 0.00% +1%
full_simple::having alloc_count 471 469 +0.43% ± 0.00% +0%
full_simple::having alloc_bytes 53,180 52,440 +1.41% ± 0.00% +1%
full_simple::having_distinct_limit alloc_bytes 54,812 54,066 +1.38% ± 0.02% +1%

Microbench: 497 of 504 measures within bounds.

Hot code per txn
path base head change
pooler 115.6 KB 115.2 KB -0.4 KB
sharding-w2 169.2 KB 169.5 KB +0.2 KB

pooler:

crate base head change
pgdog::frontend::client::query_engine 22.6 KB 22.4 KB -0.2 KB
tokio 18.4 KB 18.4 KB -0.1 KB
pgdog::backend::pool 18.2 KB 18.2 KB -0.1 KB
pgdog::frontend (other) 9.2 KB 9.2 KB +0.1 KB
aws-lc (TLS crypto) 8.6 KB 8.8 KB +0.2 KB
rustls (TLS) 7.7 KB 7.8 KB +0.1 KB
pgdog::backend::server 7.8 KB 7.6 KB -0.2 KB
other 5.8 KB 5.9 KB +0.1 KB
everything else 17.4 KB 16.9 KB -0.4 KB
function head change
pgdog::frontend::client::Client::run::{{closure}} 7.2 KB +0.0 KB
pgdog::frontend::client::query_engine::query::<impl pgdog::frontend::client::query_engine: 6.4 KB -0.2 KB
pgdog::frontend::client::query_engine::QueryEngine::handle::{{closure}} 4.9 KB +0.0 KB
pgdog::frontend::client::query_engine::query::<impl pgdog::frontend::client::query_engine: 3.8 KB +0.0 KB
tokio::runtime::task::raw::poll 3.7 KB -0.1 KB

sharding-w2:

crate base head change
pgdog::frontend::router 28.0 KB 28.1 KB +0.1 KB
pgdog::frontend::client::query_engine 23.1 KB 23.2 KB +0.1 KB
pgdog::backend::pool 19.0 KB 18.8 KB -0.2 KB
tokio 18.6 KB 18.5 KB -0.1 KB
other 10.4 KB 11.0 KB +0.6 KB
pgdog::frontend (other) 11.0 KB 10.8 KB -0.2 KB
aws-lc (TLS crypto) 10.4 KB 10.6 KB +0.2 KB
pgdog::backend::server 8.4 KB 8.3 KB -0.1 KB
everything else 40.4 KB 40.3 KB -0.1 KB
function head change
pgdog::frontend::client::Client::run::{{closure}} 8.2 KB -0.2 KB
pgdog::frontend::client::query_engine::query::<impl pgdog::frontend::client::query_engine: 6.5 KB +0.2 KB
pgdog::frontend::client::query_engine::QueryEngine::handle::{{closure}} 4.5 KB -0.1 KB
pgdog::frontend::router::parser::query::select::<impl pgdog::frontend::router::parser::que 4.1 KB +0.0 KB
tokio::runtime::task::raw::poll 3.9 KB -0.2 KB
lb flamegraph vs 3d93c4d

lb

Open interactive for hover, zoom and search.

lb removed

Open interactive.

microbench flamegraph vs 3d93c4d

microbench

Open interactive for hover, zoom and search.

microbench removed

Open interactive.

pooler flamegraph vs 3d93c4d

pooler

Open interactive for hover, zoom and search.

pooler removed

Open interactive.

sharding-w2 flamegraph vs 3d93c4d

sharding-w2

Open interactive for hover, zoom and search.

sharding-w2 removed

Open interactive.

sharding-w8 flamegraph vs 3d93c4d

sharding-w8

Open interactive for hover, zoom and search.

sharding-w8 removed

Open interactive.

Logs

lb, microbench, pooler, sharding-w2, sharding-w8, verdict.json

Compared 08fe1c7 with 3d93c4d on c7i.metal-48xl (spot).

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant