Reducing relcache memory usage 2: shrink sizeof(RelationData)

From: David Geier <geidav(dot)pg(at)gmail(dot)com>
To: Pg Hackers <pgsql-hackers(at)postgresql(dot)org>, Matthias van de Meent <boekewurm+postgres(at)gmail(dot)com>
Subject: Reducing relcache memory usage 2: shrink sizeof(RelationData)
Date: 2026-09-10 09:43:04
Message-ID: f97de758-b816-47db-96e0-0e6e2c2d3eef@gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi hackers!

[1] is concerned with reducing relcache memory usage. The approach of
[1] is to deduplicate allocations that are identical for different
relcache entries and hence are only allocated once.

To further reduce relcache memory usage we can additionally shrink the
size consumed by struct RelationData:

On master sizeof(RelationData) is 488 bytes on my x86-64 system. As
RelationData objects are allocated in an ASET backed memory context, the
used allocation class is 512 bytes plus an 8 byte header which makes 520
bytes per relcache entry. This patch reduces the size to 256 bytes plus
the 8 byte heades. This is a net saving of 256 bytes per relcache entry.

I've tested the patch set with a real world database schema consisting
of 38,966 tables and 148,468 indexes. I used a PostgreSQL release build
and ran the following query to load all relations, including catalog
relations, into the relcache of a single backend:

SET max_parallel_workers_per_gather = 0;
SELECT COUNT(pg_relation_size(oid)) FROM pg_class WHERE relkind IN ('r',
'i');

Disabling parallelism is crucial so that all relcache entries are truly
created in the backend process and not inside the workers.

All RelationData objects are allocated in CacheMemoryContext. The size
of CacheMemoryContext can be inspected via pg_backend_memory_contexts:

branch | used_bytes
--------|---------------------------------
master | 617,984,696 bytes = ~589.36 MiB
patched | 562,507,232 bytes = ~536.45 MiB

We save ~53 MiB or ~10% of CacheMemoryContext per backend!

The attached patch set passes tests and consists of the following
individual patches:

- 0001: Moves the definition of RelationData into utils/rel_internal.h.
Otherwise, gen_node_support.pl fails because it doesn't support parsing
the anonymous unions introduced in 0002.
=> sizeof(RelationData) == 488 bytes

- 0002: Introduces a union between table-like and index-only cached
state which use is mutually exclusive, depending on the relation type.
=> sizeof(RelationData) == 360 bytes

- 0003: Packs RelationData by reordering members according to alignment
requirements to minimize compiler-inserted padding.
=> sizeof(RelationData) == 312 bytes

- 0004: Removes the rd_lockinfo member. RelationGetLockRelId() now
computes it when needed. This also removes RelationInitLockInfo() and
the initialization work associated with it.
=> sizeof(RelationData) == 304 bytes

- 0005: Replaces the embedded partition key, descriptors, partition
qual, validity flag, and memory contexts with one lazily allocated
RelationPartitionInfo pointer.
=> sizeof(RelationData) == 264 bytes

- 0006: Removes rd_fkeyvalid by using RELCACHE_FKEYLIST_NOT_LOADED as
the initial state of rd_fkeylist. NIL continues to mean that the list
was computed and no foreign keys were found. This patch is not strictly
needed because it currently doesn't further reduce the size. The same we
could with RelationPartitionInfo::partcheckvalid.
=> sizeof(RelationData) == 264 bytes

- 0007: Removes rd_index member that always pointed inside
rd_indextuple. Callers now use RelationGetIndex(), which applies
GETSTRUCT() to that tuple.
=> sizeof(RelationData) == 256 bytes

- 0008: Adds a static assertion that sizeof(RelationData) <= 256.
=> sizeof(RelationData) == 256 bytes

Note that the necessity to arrive at 256 bytes stems from ASET's
allocation granularity being powers of two. To profit from further size
reductions of RelationData, we would need to use a SLAB memory context
because it's unlikely that we'll get to 128 bytes.

--
David Geier

[1]
https://www.postgresql.org/message-id/flat/CAEze2WiPyruOtUOSyRUV8mQssjmYwno0M6hkxC_iUpH-%3DW8WcA%40mail.gmail.com

Attachment Content-Type Size
v1-0001-Move-RelationData-to-new-include.patch text/plain 21.5 KB
v1-0002-Use-union.patch text/plain 23.1 KB
v1-0003-Packing-RelationData.patch text/plain 7.6 KB
v1-0004-Remove-rd_lockinfo.patch text/plain 20.4 KB
v1-0005-Move-out-partition-members.patch text/plain 20.6 KB
v1-0006-Remove-rd_fkeyvalid.patch text/plain 4.1 KB
v1-0007-Remove-rd_index.patch text/plain 51.5 KB
v1-0008-Add-static-assert-for-size.patch text/plain 754 bytes

Browse pgsql-hackers by date

  From Date Subject
Next Message Ashutosh Bapat 2026-09-10 09:44:22 Re: PGQ catalog representation and pg_dump support
Previous Message shveta malik 2026-09-10 09:42:09 Re: Review items for EXCEPT TABLE publication