Skip to content

Measure PG18 connection memory overhead on Ubuntu #37

@NikolayS

Description

@NikolayS

Summary

I repeated Andres Freund's 2020 connection-memory measurement method on PostgreSQL 18.3 on Ubuntu 24.04, with both huge_pages = off and huge_pages = on.

Reference method: https://blog.anarazel.de/2020/10/07/measuring-the-memory-overhead-of-a-postgres-connection/

Short version: ps/top RSS is still the wrong metric. Using Pss_Anon from /proc/$pid/smaps_rollup plus VmPTE from /proc/$pid/status, a PG18 backend running a simple pgbench read-only workload on this host was:

huge_pages shared_buffers median overhead range
off 1GB 2.64 MiB 2.17–2.92 MiB
on 1GB 1.51 MiB 1.51–1.52 MiB

For reference, an earlier huge_pages = off run with shared_buffers = 2GB measured 2.33 MiB median across 10 sampled pgbench backends. The final comparison table above uses 1GB for both modes because this host could allocate only 594 huge pages (~1.16 GiB) without reboot/compaction.

Environment

Host: Linux openclaw-server 6.8.0-94-generic #96-Ubuntu SMP PREEMPT_DYNAMIC Fri Jan 9 20:36:55 UTC 2026 x86_64
OS: Ubuntu 24.04.3 LTS
PostgreSQL: PostgreSQL 18.3 (Ubuntu 18.3-1.pgdg24.04+1)
THP: always [madvise] never
Huge page size: 2048 kB

Cluster settings for the comparable runs:

max_connections = 1100
shared_buffers = 1GB
work_mem = 4MB
jit = on

For the huge_pages = on run, host huge pages were temporarily raised from 0 to 594 and then restored to 0 after the experiment.

Workload

Throwaway PG18 cluster under /tmp, Unix socket only.

pgbench -i -s 100 bench
pgbench -S -M prepared -c 1024 -j 16 -T 90 bench

At sampling time in both runs:

connected pgbench backends: 1024
pg_stat_activity rows: 1033

Measurement method

Same approximation as the blog post:

per-connection overhead ~= Pss_Anon + VmPTE

Where:

  • Pss_Anon comes from /proc/$pid/smaps_rollup
  • VmPTE comes from /proc/$pid/status
  • RssFile is ignored because it is mostly executable/shared library mappings
  • RssShmem / Pss_Shmem is not counted as private connection memory

Results: huge_pages = off

Cluster:

huge_pages = off
shared_buffers = 131072 8kB  # 1GB
nr_hugepages = 0

Fresh sleeping connection before pgbench:

RssAnon:        3344 kB
RssFile:        8576 kB
RssShmem:       8448 kB
VmPTE:           236 kB
HugetlbPages:      0 kB
Pss:            8296 kB
Pss_Anon:       1510 kB
Pss_File:       3055 kB
Pss_Shmem:      3731 kB

Sampled pgbench backends:

pid RssAnon kB RssFile kB RssShmem kB HugetlbPages kB Pss_Anon kB VmPTE kB approx overhead MiB
177527 3216 9088 29184 0 1398 820 2.17
177528 3216 9088 31360 0 1398 868 2.21
177529 3344 8960 34560 0 1398 1064 2.40
177531 3216 9088 39424 0 1398 1132 2.47
177532 3344 9088 42112 0 1410 1236 2.58
177533 3344 9088 46464 0 1398 1360 2.69
177534 3472 9088 48128 0 1410 1372 2.72
177537 3472 9088 53504 0 1398 1504 2.83
177538 3344 8960 57984 0 1398 1580 2.91
177539 3344 9088 58240 0 1398 1596 2.92

Summary:

min:    2218 kB = 2.17 MiB
median: 2702 kB = 2.64 MiB
avg:    2653.6 kB = 2.59 MiB
max:    2994 kB = 2.92 MiB

pgbench:

number of transactions actually processed: 3176975
number of failed transactions: 0 (0.000%)
latency average = 28.249 ms
tps = 36249.676823 (without initial connection time)

Results: huge_pages = on

Cluster:

huge_pages = on
shared_buffers = 131072 8kB  # 1GB
nr_hugepages = 594
HugePages_Total = 594
HugePages_Free at sampling = 108

Fresh sleeping connection before pgbench:

RssAnon:        3424 kB
RssFile:        8320 kB
RssShmem:          0 kB
VmPTE:           144 kB
HugetlbPages:  55296 kB
Pss:            4598 kB
Pss_Anon:       1508 kB
Pss_File:       2991 kB
Pss_Shmem:        99 kB

Sampled pgbench backends:

pid RssAnon kB RssFile kB RssShmem kB HugetlbPages kB Pss_Anon kB VmPTE kB approx overhead MiB
157621 3296 8960 0 516096 1398 144 1.51
157622 3424 9088 128 438272 1398 144 1.51
157626 3280 9088 0 561152 1398 144 1.51
157627 3424 9088 128 546816 1398 144 1.51
157628 3424 9088 128 743424 1410 144 1.52
157629 3424 9088 128 741376 1398 144 1.51
157634 3296 9088 0 722944 1398 144 1.51
157637 3424 9088 128 958464 1410 144 1.52
157641 3412 9088 128 776192 1410 144 1.52
157642 3424 9088 128 786432 1398 144 1.51

Summary:

min:    1542 kB = 1.51 MiB
median: 1542 kB = 1.51 MiB
avg:    1545.6 kB = 1.51 MiB
max:    1554 kB = 1.52 MiB

pgbench:

number of transactions actually processed: 4344743
number of failed transactions: 0 (0.000%)
latency average = 20.729 ms
tps = 49399.941112 (without initial connection time)

Notes

  • Huge pages reduced VmPTE from hundreds/thousands of kB down to a stable 144 kB in this run. That's the main difference, same as Andres described in 2020.
  • HugetlbPages looks huge per backend and should not be interpreted as private per-connection memory. The approximation intentionally uses Pss_Anon + VmPTE.
  • RSS remains misleading: with huge_pages = off, RssShmem grows as the backend touches shared buffers; with huge_pages = on, the same shared buffer footprint moves to HugetlbPages and page-table overhead collapses.
  • On this host, PG18 connection overhead in this workload is roughly 2.6 MiB with huge pages off and 1.5 MiB with huge pages on.

Reproducer

PG=/usr/lib/postgresql/18/bin
BASE=/tmp/pg18-conn-overhead
DATA=$BASE/data
SOCK=$BASE/socket
PORT=55418

rm -rf "$BASE"
mkdir -p "$SOCK"
"$PG/initdb" -D "$DATA" --no-locale --encoding=UTF8
cat >> "$DATA/postgresql.conf" <<EOF
port = $PORT
listen_addresses = ''
unix_socket_directories = '$SOCK'
max_connections = 1100
shared_buffers = '1GB'
huge_pages = on   # or off
fsync = off
synchronous_commit = off
full_page_writes = off
EOF
cat > "$DATA/pg_hba.conf" <<EOF
local all all trust
EOF
"$PG/pg_ctl" -D "$DATA" -l "$BASE/postgres.log" -w start
export PGHOST=$SOCK PGPORT=$PORT PGDATABASE=postgres PGUSER="$USER"
"$PG/createdb" bench
export PGDATABASE=bench
"$PG/pgbench" -i -s 100 bench
"$PG/pgbench" -S -M prepared -c 1024 -j 16 -T 90 bench

# During the pgbench run, pick backend PIDs from pg_stat_activity and inspect:
grep -E '^(Rss|HugetlbPages|VmPTE)' /proc/$pid/status
grep -E '^(Pss|Pss_Anon|Pss_File|Pss_Shmem):' /proc/$pid/smaps_rollup

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions