Skip to content

perf: 3x smaller shared stats copies, 2-field copies on most messages - #1993

Merged
jkaczman merged 1 commit into
mainfrom
jk-server-stats-optim
Nov 9, 2026
Merged

jkaczman merged 1 commit into
mainfrom
jk-server-stats-optim

Conversation

@jkaczman

@jkaczman jkaczman commented Nov 8, 2026

Copy link
Copy Markdown
Contributor

Background:

  • On all messages to and from Postgres, we copy the stats of the server connection into a shared copy behind a mutex, which admin commands (e.g., SHOW SERVERS) and metrics use as the source of their data
  • feat: track server last sent/received #728 started copying on all messages to ensure that SHOW SERVERS shows the current last message codes (last_sent and last_received), e.g., to debug stuck connections.
  • However, ever since SHOW SERVERS was added (way back when!), the shared and per-connection stats BOTH used a singular type (ServerStats) to store their stats, so every single one of those shared copies unnecessarily included some fields only relevant to the per-connection store (e.g., the last_checkout counters and the timers), meaning that there were 448 extra bytes being copied.

Why do these extra 448 bytes that the shared copy never use (664 − 216) cost us any performance?

The stats are copied with memcpy, which uses 8 instructions for every 64 bytes copied. If you take 448 bytes, divide by 64, multiply by 8, that's 56 instructions for every single copy performed. The stats are copied on ALL messages to and from Postgres (e.g., once per row), so these copies add up fast!

The changes below cut our instructions per request by:

  • 4.2% for query that returns 10 rows (and our memory writes by 6.1%)
  • 9.0% for query that returns 1,000 rows (and our memory writes by 12.9%)

Now,

  • a new, smaller type for the shared copy (SharedServerStats), which only includes fields actually used (the 216 bytes)
  • most messages only change two of the fields (the byte count and the last message code), so for those messages, this now only copies those two fields into the shared copy. If we get a function that changes a field of SharedServerStats other than those two (e.g., query() adds 1 to queries), it has to go through shared_mut(), which sets a dirty flag. If the flag is set, the next message copies all 216 bytes. As it's more rare, this further improves performance for most cases.

Also, we previously counted 18 counters (e.g., parse and rows_inserted) twice, once since the last checkout (last_checkout, which SHOW STATS adds up at check-in) and once over the life of the connection (total). However, only tests were using the counts in total, so I fixed the redundancy.

@jkaczman
jkaczman added this pull request to stack #1994 November 8, 2026 21:37
@codecov

codecov Bot commented Nov 8, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@jkaczman
jkaczman force-pushed the jk-server-stats-optim branch from faa04c9 to 27a26e6 Compare November 8, 2026 22:48
@github-actions

github-actions Bot commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

Metal bench (arm64) failed. 2 of 475 measures outside bounds.

suite throughput cpu/txn user cycles/txn user instr/txn kernel cycles/txn kernel instr/txn ctx/txn
pooler 18.63 (-1.4% ± 0.9%) 17,732 (-2.7% ± 0.3%) 31,961 (-2.9% ± 0.06%) 29,754 (-0.9% ± 1.8%) 51,218 (+0.3% ± 0.3%)
lb 19.2 (-1.4% ± 1.0%) 18,219 (-2.9% ± 0.3%) 32,867 (-2.8% ± 0.03%) 30,529 (-0.9% ± 1.6%) 52,286 (+0.2% ± 0.4%)
sharding-w2 31.46 (-1.6% ± 1.8%) 42,670 (-2.5% ± 1.6%) 97,321 (-1.5% ± 0.02%) 38,698 (-1.6% ± 4.2%) 53,722 (+0.1% ± 0.09%) 1.555 (+0.3% ± 0.4%)
sharding-w8 250,545 (+1.4% ± 1.5%) 28.49 (-1.6% ± 2.0%) 43,938 (-2.5% ± 1.6%) 52,846 (-2.7% ± 0.04%) 34,726 (-0.6% ± 2.6%) 42,358 (+0.0% ± 0.08%) 0.1714 (+1.5% ± 3.0%)
Counters per txn
suite frontend stall l1i refill backend stall
pooler 7,058 (-3.1% ± 0.9%) 2,558 (-0.5% ± 0.4%)
lb 7,317 (-3.4% ± 1.3%) 2,608 (-0.5% ± 0.3%)
sharding-w2 16,304 (-3.6% ± 4.7%) 3,946 (-1.5% ± 2.9%)
sharding-w8 19,023 (-4.1% ± 1.2%) 3,958 (-1.6% ± 0.4%)
benchmark measure result baseline change bound
extended_evicting::set instructions 16,520 16,244 +1.70% ± 0.00% +1%
simple_cold::set instructions 14,915 14,628 +1.96% ± 0.00% +1%

Microbench: 460 of 462 measures within bounds.

  • full_extended_cached: 20 of 22 benches faster, median -2.2% ± 0.0%
  • full_named_cached: 22 of 22 benches faster, median -2.4% ± 0.0%
  • full_simple: 22 of 22 benches faster, median -1.6% ± 0.0%
Microbench latency: 4 benches changed more than 3%
benchmark latency baseline change instructions change
full_extended_cached::cte 15,310 ns 15,878 ns -3.56% ± 1.51% -2.45% ± 0.00%
full_simple::select_all_shards 20,428 ns 21,116 ns -3.26% ± 0.68% -2.69% ± 0.00%
full_extended_cached::select_in_list 25,524 ns 26,346 ns -3.12% ± 0.63% -2.57% ± 0.00%
simple_cold::lookup 13,994 ns 14,436 ns -3.06% ± 0.79% -1.12% ± 0.00%
Hot code per txn
path base head change
pooler 111.6 KB 110.9 KB -0.7 KB
sharding-w2 163.8 KB 164.1 KB +0.3 KB

pooler:

crate base head change
pgdog::frontend::client::query_engine 19.9 KB 20.0 KB +0.1 KB
tokio 18.5 KB 18.1 KB -0.4 KB
pgdog::backend::pool 17.2 KB 17.4 KB +0.2 KB
aws-lc (TLS crypto) 10.5 KB 10.6 KB +0.1 KB
rustls (TLS) 8.2 KB 8.4 KB +0.1 KB
pgdog::frontend (other) 7.8 KB 7.8 KB +0.0 KB
pgdog::backend::server 7.1 KB 7.0 KB -0.1 KB
other 6.2 KB 6.1 KB -0.1 KB
everything else 16.2 KB 15.6 KB -0.6 KB
function head change
pgdog::frontend::client::Client::run::{{closure}} 5.4 KB +0.0 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.4 KB +0.0 KB
tokio::runtime::task::raw::poll 3.2 KB +0.0 KB
pgdog::frontend::client::query_engine::query::<impl pgdog::frontend::client::query_engine: 2.9 KB +0.1 KB

sharding-w2:

crate base head change
pgdog::frontend::router 26.8 KB 26.8 KB -0.1 KB
pgdog::frontend::client::query_engine 22.4 KB 22.2 KB -0.2 KB
tokio 18.7 KB 18.5 KB -0.2 KB
pgdog::backend::pool 17.8 KB 18.0 KB +0.2 KB
aws-lc (TLS crypto) 13.2 KB 13.2 KB -0.1 KB
other 9.6 KB 9.8 KB +0.1 KB
pgdog::frontend (other) 8.4 KB 8.6 KB +0.1 KB
rustls (TLS) 8.1 KB 8.1 KB +0.1 KB
everything else 38.8 KB 39.1 KB +0.3 KB
function head change
pgdog::frontend::client::Client::run::{{closure}} 6.1 KB +0.0 KB
pgdog::frontend::client::query_engine::query::<impl pgdog::frontend::client::query_engine: 5.2 KB -0.2 KB
pgdog::frontend::client::query_engine::QueryEngine::handle::{{closure}} 4.0 KB +0.0 KB
pgdog::frontend::router::parser::query::select::<impl pgdog::frontend::router::parser::que 3.4 KB +0.1 KB
tokio::runtime::task::raw::poll 3.1 KB -0.2 KB
lb flamegraph vs eaab055

lb

Open interactive for hover, zoom and search.

lb removed

Open interactive.

microbench flamegraph vs eaab055

microbench

Open interactive for hover, zoom and search.

microbench removed

Open interactive.

pooler flamegraph vs eaab055

pooler

Open interactive for hover, zoom and search.

pooler removed

Open interactive.

sharding-w2 flamegraph vs eaab055

sharding-w2

Open interactive for hover, zoom and search.

sharding-w2 removed

Open interactive.

sharding-w8 flamegraph vs eaab055

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 d653c73 with eaab055 on m8g.metal-24xl (spot).

@github-actions

github-actions Bot commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

Metal bench (x86) passed. All 475 measures within bounds.

suite throughput cpu/txn user cycles/txn user instr/txn kernel cycles/txn kernel instr/txn ctx/txn
pooler 25.8 (-0.8% ± 0.3%) 18,805 (-2.0% ± 0.2%) 33,868 (-1.6% ± 0.05%) 33,338 (-0.1% ± 0.4%) 36,567 (+0.4% ± 0.5%)
lb 26.2 (-0.8% ± 0.3%) 19,385 (-2.1% ± 0.1%) 34,828 (-1.6% ± 0.04%) 33,395 (-0.3% ± 0.4%) 37,288 (+0.1% ± 0.6%)
sharding-w2 42.01 (-0.8% ± 1.1%) 47,330 (-1.7% ± 0.9%) 101,236 (-0.9% ± 0.06%) 38,348 (+0.0% ± 2.8%) 40,236 (+0.0% ± 0.4%) 1.529 (+0.4% ± 0.3%)
sharding-w8 209,030 (+1.0% ± 0.6%) 32.51 (-1.7% ± 0.5%) 45,249 (-2.9% ± 1.2%) 55,572 (-1.6% ± 0.05%) 31,038 (+0.0% ± 0.4%) 32,513 (+0.0% ± 0.1%) 0.1735 (+2.3% ± 1.9%)
Counters per txn
suite frontend stall l1i refill backend stall
pooler 3,558 (-3.9% ± 0.6%) 1,199 (-1.1% ± 0.5%) 36,911 (-2.1% ± 0.2%)
lb 3,763 (-3.5% ± 0.5%) 1,240 (-1.1% ± 0.5%) 37,528 (-2.6% ± 0.5%)
sharding-w2 6,337 (+2.8% ± 1.2%) 1,648 (+1.7% ± 1.1%) 113,494 (-4.5% ± 2.4%)
sharding-w8 3,466 (-3.6% ± 0.6%) 1,035 (+1.8% ± 1.1%) 169,396 (-4.0% ± 2.0%)

Microbench: all 462 measures within bounds.

  • full_extended_cached: 22 of 22 benches faster, median -1.3% ± 0.0%
  • full_named_cached: 22 of 22 benches faster, median -1.2% ± 0.0%
Microbench latency: 11 benches changed more than 3%
benchmark latency baseline change instructions change
extended_cold::delete_all_shards 3,121 ns 3,247 ns -3.89% ± 0.19% +0.00% ± 0.00%
full_simple::begin 4,984 ns 5,185 ns -3.87% ± 0.91% +0.00% ± 0.00%
simple_cold::delete_all_shards 2,911 ns 3,020 ns -3.58% ± 1.84% +0.00% ± 0.00%
extended_evicting::delete_all_shards 3,245 ns 3,364 ns -3.55% ± 0.77% +0.00% ± 0.00%
extended_cold::begin 2,250 ns 2,330 ns -3.42% ± 0.91% +0.00% ± 0.00%
extended_evicting::begin 2,399 ns 2,481 ns -3.30% ± 1.30% +0.00% ± 0.00%
full_simple::set 6,872 ns 7,100 ns -3.21% ± 0.29% +0.00% ± 0.00%
full_simple::select_by_key 15,616 ns 16,126 ns -3.16% ± 0.37% -1.15% ± 0.00%
simple_cold::begin 2,062 ns 2,129 ns -3.13% ± 1.55% +0.00% ± 0.00%
full_simple::update_all_shards 13,884 ns 14,324 ns -3.07% ± 0.65% -0.97% ± 0.00%
simple_cold::select_omni 3,669 ns 3,785 ns -3.06% ± 1.04% +0.00% ± 0.00%
Hot code per txn
path base head change
pooler 110.0 KB 111.1 KB +1.1 KB
sharding-w2 162.6 KB 163.2 KB +0.7 KB

pooler:

crate base head change
pgdog::frontend::client::query_engine 20.4 KB 20.7 KB +0.2 KB
pgdog::backend::pool 18.4 KB 18.6 KB +0.2 KB
tokio 18.3 KB 18.4 KB +0.1 KB
pgdog::frontend (other) 9.4 KB 9.5 KB +0.1 KB
aws-lc (TLS crypto) 8.7 KB 8.9 KB +0.2 KB
rustls (TLS) 7.8 KB 8.1 KB +0.3 KB
pgdog::backend::server 6.8 KB 6.9 KB +0.1 KB
other 5.8 KB 5.8 KB +0.1 KB
everything else 14.5 KB 14.2 KB -0.3 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: 5.8 KB -0.1 KB
pgdog::frontend::client::query_engine::QueryEngine::handle::{{closure}} 4.7 KB +0.1 KB
tokio::runtime::task::raw::poll 3.6 KB +0.0 KB
pgdog::frontend::client::query_engine::query::<impl pgdog::frontend::client::query_engine: 3.2 KB +0.2 KB

sharding-w2:

crate base head change
pgdog::frontend::router 26.2 KB 26.6 KB +0.4 KB
pgdog::frontend::client::query_engine 23.5 KB 23.6 KB +0.1 KB
pgdog::backend::pool 18.6 KB 18.6 KB +0.1 KB
tokio 18.6 KB 18.3 KB -0.3 KB
pgdog::frontend (other) 10.5 KB 10.6 KB +0.1 KB
other 10.2 KB 10.4 KB +0.1 KB
aws-lc (TLS crypto) 10.3 KB 10.3 KB +0.0 KB
pgdog::backend::server 7.6 KB 7.8 KB +0.2 KB
everything else 37.1 KB 37.1 KB +0.0 KB
function head change
pgdog::frontend::client::Client::run::{{closure}} 8.0 KB -0.1 KB
pgdog::frontend::client::query_engine::query::<impl pgdog::frontend::client::query_engine: 5.9 KB +0.1 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.2 KB +0.0 KB
tokio::runtime::task::raw::poll 3.9 KB -0.2 KB
lb flamegraph vs eaab055

lb

Open interactive for hover, zoom and search.

lb removed

Open interactive.

microbench flamegraph vs eaab055

microbench

Open interactive for hover, zoom and search.

microbench removed

Open interactive.

pooler flamegraph vs eaab055

pooler

Open interactive for hover, zoom and search.

pooler removed

Open interactive.

sharding-w2 flamegraph vs eaab055

sharding-w2

Open interactive for hover, zoom and search.

sharding-w2 removed

Open interactive.

sharding-w8 flamegraph vs eaab055

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 d653c73 with eaab055 on m7i.metal-48xl (spot).

@github-actions

github-actions Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

Metal bench PGO (arm64).

6 rounds of eaab055 vs d653c73.

suite measure base head launches per side delta (95% CI)
pooler throughput 16,994 16,994 18 +0.00% ± 0.00%
pooler cpu-per-txn 17.94 17.74 18 -1.10% ± 0.37%
pooler user-cycles-per-txn 16,524 16,039 18 -2.94% ± 0.16%
pooler user-instructions-per-txn 30,446 29,624 18 -2.70% ± 0.10%
pooler kernel-instructions-per-txn 51,508 51,518 18 +0.02% ± 0.37%
pooler kernel-cycles-per-txn 28,829 28,721 18 -0.37% ± 0.58%
lb throughput 15,997 15,998 18 +0.00% ± 0.00%
lb cpu-per-txn 18.57 18.32 18 -1.34% ± 0.38%
lb user-cycles-per-txn 17,174 16,602 18 -3.33% ± 0.08%
lb user-instructions-per-txn 31,268 30,487 18 -2.50% ± 0.05%
lb kernel-instructions-per-txn 52,632 52,581 18 -0.10% ± 0.39%
lb kernel-cycles-per-txn 29,537 29,477 18 -0.20% ± 0.81%
sharding-w8 throughput 279,761 282,888 4 +1.11% ± 2.29%
sharding-w8 cpu-per-txn 24.56 24.22 4 -1.36% ± 2.64%
sharding-w8 user-cycles-per-txn 37,912 36,721 4 -3.14% ± 1.51%
sharding-w8 user-instructions-per-txn 47,403 46,153 4 -2.64% ± 0.02%
sharding-w8 kernel-instructions-per-txn 42,514 42,506 4 -0.02% ± 0.10%
sharding-w8 kernel-cycles-per-txn 29,696 29,936 4 +0.80% ± 4.22%
sharding-w8 ctx-switches-per-txn 0.2127 0.2149 4 +1.02% ± 3.99%
sharding-w2 throughput 9,999 9,999 4 +0.00% ± 0.00%
sharding-w2 cpu-per-txn 26.94 26.43 4 -1.90% ± 0.74%
sharding-w2 user-cycles-per-txn 35,041 33,943 4 -3.13% ± 0.90%
sharding-w2 user-instructions-per-txn 85,210 83,816 4 -1.64% ± 0.01%
sharding-w2 kernel-instructions-per-txn 53,706 53,722 4 +0.03% ± 0.02%
sharding-w2 kernel-cycles-per-txn 33,763 33,424 4 -1.00% ± 1.34%
sharding-w2 ctx-switches-per-txn 1.571 1.573 4 +0.15% ± 0.09%

Compared d653c73 with eaab055 on c8g.metal-24xl (on-demand).

@github-actions

github-actions Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

Metal bench PGO (x86).

6 rounds of eaab055 vs d653c73.

suite measure base head launches per side delta (95% CI)
pooler throughput 16,994 16,991 18 -0.02% ± 0.05%
pooler cpu-per-txn 32.65 32.49 18 -0.49% ± 0.28%
pooler user-cycles-per-txn 19,564 19,164 18 -2.04% ± 1.16%
pooler user-instructions-per-txn 27,025 26,908 18 -0.44% ± 0.02%
pooler kernel-instructions-per-txn 38,972 38,993 18 +0.05% ± 0.08%
pooler kernel-cycles-per-txn 55,189 55,159 18 -0.05% ± 0.24%
lb throughput 15,997 15,997 18 -0.00% ± 0.00%
lb cpu-per-txn 33.56 33.38 18 -0.53% ± 0.21%
lb user-cycles-per-txn 20,580 20,058 18 -2.53% ± 1.09%
lb user-instructions-per-txn 28,018 27,909 18 -0.39% ± 0.02%
lb kernel-instructions-per-txn 39,597 39,623 18 +0.07% ± 0.10%
lb kernel-cycles-per-txn 55,978 55,982 18 +0.01% ± 0.24%
sharding-w8 throughput 156,071 157,258 4 +0.76% ± 2.31%
sharding-w8 cpu-per-txn 45.32 45.05 4 -0.61% ± 1.43%
sharding-w8 user-cycles-per-txn 53,272 51,958 4 -2.47% ± 1.78%
sharding-w8 user-instructions-per-txn 44,492 43,998 4 -1.11% ± 0.25%
sharding-w8 kernel-instructions-per-txn 34,229 34,305 4 +0.22% ± 0.17%
sharding-w8 kernel-cycles-per-txn 61,377 61,862 4 +0.79% ± 1.74%
sharding-w8 ctx-switches-per-txn 0.195 0.2031 4 +4.17% ± 5.53%
sharding-w2 throughput 9,999 9,999 4 -0.00% ± 0.00%
sharding-w2 cpu-per-txn 51.16 50.5 4 -1.30% ± 0.59%
sharding-w2 user-cycles-per-txn 43,069 41,685 4 -3.21% ± 1.27%
sharding-w2 user-instructions-per-txn 87,352 86,474 4 -1.01% ± 0.07%
sharding-w2 kernel-instructions-per-txn 43,288 43,300 4 +0.03% ± 0.20%
sharding-w2 kernel-cycles-per-txn 68,856 68,429 4 -0.62% ± 0.65%
sharding-w2 ctx-switches-per-txn 1.431 1.437 4 +0.36% ± 0.10%

Compared d653c73 with eaab055 on c7a.metal-48xl (spot).

Base automatically changed from jk-microbench-paused-clock to main November 9, 2026 16:33
@jkaczman
jkaczman force-pushed the jk-server-stats-optim branch from 27a26e6 to d653c73 Compare November 9, 2026 16:33
@jkaczman
jkaczman merged commit 030f9a5 into main Nov 9, 2026
47 of 49 checks passed
@jkaczman
jkaczman deleted the jk-server-stats-optim branch November 9, 2026 18:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants