From 9ebe1c4f2c7cecd0fc2d02afd87a74f6ba44569e Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Sat, 4 Apr 2026 02:40:20 +0300 Subject: [PATCH] Merge init and max size options on shmem hash tables Replace the separate init and max size options with a single size option. We didn't make much use of the feature, all callers except the ones in wait_event.c already used the same size for both, and the hash tables in wait_event.c are small so there's little harm in just allocating them to the max size. The only reason why you might want to not reserve the max size upfront is to make the memory available for other hash tables to grow beyond their max size. Letting hash tables grow much beyond their max size is bad for performance, however, because we cannot resize the directory, and we never had very much "wiggle room" to grow to anyway so you couldn't really rely on it. We recently marked the LOCK and PROCLOCK tables with HAS_FIXED_SIZE, so there's nothing left in core that would benefit from more unallocated shared memory. Reviewed-by: Tomas Vondra Discussion: https://www.postgresql.org/message-id/01ab1d41-3eda-4705-8bbd-af898f5007f1@iki.fi --- .../pg_stat_statements/pg_stat_statements.c | 2 +- src/backend/storage/buffer/buf_table.c | 2 +- src/backend/storage/ipc/shmem.c | 16 ++++---------- src/backend/storage/lmgr/lock.c | 2 -- src/backend/storage/lmgr/predicate.c | 3 --- src/backend/utils/activity/wait_event.c | 22 ++++++++----------- src/include/storage/shmem.h | 2 +- 7 files changed, 16 insertions(+), 33 deletions(-) diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c index 7975476b890..5494d41dca1 100644 --- a/contrib/pg_stat_statements/pg_stat_statements.c +++ b/contrib/pg_stat_statements/pg_stat_statements.c @@ -561,7 +561,7 @@ pgss_shmem_startup(void) info.keysize = sizeof(pgssHashKey); info.entrysize = sizeof(pgssEntry); pgss_hash = ShmemInitHash("pg_stat_statements hash", - pgss_max, pgss_max, + pgss_max, &info, HASH_ELEM | HASH_BLOBS); diff --git a/src/backend/storage/buffer/buf_table.c b/src/backend/storage/buffer/buf_table.c index 23d85fd32e2..d04ef74b850 100644 --- a/src/backend/storage/buffer/buf_table.c +++ b/src/backend/storage/buffer/buf_table.c @@ -60,7 +60,7 @@ InitBufTable(int size) info.num_partitions = NUM_BUFFER_PARTITIONS; SharedBufHash = ShmemInitHash("Shared Buffer Lookup Table", - size, size, + size, &info, HASH_ELEM | HASH_BLOBS | HASH_PARTITION | HASH_FIXED_SIZE); } diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c index a5b7360e2dc..85757a35823 100644 --- a/src/backend/storage/ipc/shmem.c +++ b/src/backend/storage/ipc/shmem.c @@ -330,14 +330,7 @@ ShmemAddrIsValid(const void *addr) * table at once. (In practice, all creations are done in the postmaster * process; child processes should always be attaching to existing tables.) * - * max_size is the estimated maximum number of hashtable entries. This is - * not a hard limit, but the access efficiency will degrade if it is - * exceeded substantially (since it's used to compute directory size and - * the hash table buckets will get overfull). - * - * init_size is the number of hashtable entries to preallocate. For a table - * whose maximum size is certain, this should be equal to max_size; that - * ensures that no run-time out-of-shared-memory failures can occur. + * nelems is the maximum number of hashtable entries. * * *infoP and hash_flags must specify at least the entry sizes and key * comparison semantics (see hash_create()). Flag bits and values specific @@ -350,8 +343,7 @@ ShmemAddrIsValid(const void *addr) */ HTAB * ShmemInitHash(const char *name, /* table string name for shmem index */ - int64 init_size, /* initial table size */ - int64 max_size, /* max size of the table */ + int64 nelems, /* size of the table */ HASHCTL *infoP, /* info about key and bucket size */ int hash_flags) /* info about infoP */ { @@ -365,7 +357,7 @@ ShmemInitHash(const char *name, /* table string name for shmem index */ * * The shared memory allocator must be specified too. */ - infoP->dsize = infoP->max_dsize = hash_select_dirsize(max_size); + infoP->dsize = infoP->max_dsize = hash_select_dirsize(nelems); infoP->alloc = ShmemHashAlloc; infoP->alloc_arg = NULL; hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE; @@ -385,7 +377,7 @@ ShmemInitHash(const char *name, /* table string name for shmem index */ /* Pass location of hashtable header to hash_create */ infoP->hctl = (HASHHDR *) location; - return hash_create(name, init_size, infoP, hash_flags); + return hash_create(name, nelems, infoP, hash_flags); } /* diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c index aa0f7bf4a1c..9dae407981f 100644 --- a/src/backend/storage/lmgr/lock.c +++ b/src/backend/storage/lmgr/lock.c @@ -463,7 +463,6 @@ LockManagerShmemInit(void) info.num_partitions = NUM_LOCK_PARTITIONS; LockMethodLockHash = ShmemInitHash("LOCK hash", - max_table_size, max_table_size, &info, HASH_ELEM | HASH_BLOBS | @@ -482,7 +481,6 @@ LockManagerShmemInit(void) info.num_partitions = NUM_LOCK_PARTITIONS; LockMethodProcLockHash = ShmemInitHash("PROCLOCK hash", - max_table_size, max_table_size, &info, HASH_ELEM | HASH_FUNCTION | diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c index efa47ec1684..e003fa5b107 100644 --- a/src/backend/storage/lmgr/predicate.c +++ b/src/backend/storage/lmgr/predicate.c @@ -1182,7 +1182,6 @@ PredicateLockShmemInit(void) info.num_partitions = NUM_PREDICATELOCK_PARTITIONS; PredicateLockTargetHash = ShmemInitHash("PREDICATELOCKTARGET hash", - max_predicate_lock_targets, max_predicate_lock_targets, &info, HASH_ELEM | HASH_BLOBS | @@ -1218,7 +1217,6 @@ PredicateLockShmemInit(void) max_predicate_locks = max_predicate_lock_targets * 2; PredicateLockHash = ShmemInitHash("PREDICATELOCK hash", - max_predicate_locks, max_predicate_locks, &info, HASH_ELEM | HASH_FUNCTION | @@ -1297,7 +1295,6 @@ PredicateLockShmemInit(void) info.entrysize = sizeof(SERIALIZABLEXID); SerializableXidHash = ShmemInitHash("SERIALIZABLEXID hash", - max_serializable_xacts, max_serializable_xacts, &info, HASH_ELEM | HASH_BLOBS | diff --git a/src/backend/utils/activity/wait_event.c b/src/backend/utils/activity/wait_event.c index e5a2289f0b0..2b76967776c 100644 --- a/src/backend/utils/activity/wait_event.c +++ b/src/backend/utils/activity/wait_event.c @@ -56,16 +56,14 @@ uint32 *my_wait_event_info = &local_my_wait_event_info; * For simplicity, we use the same ID counter across types of custom events. * We could end that anytime the need arises. * - * The size of the hash table is based on the assumption that - * WAIT_EVENT_CUSTOM_HASH_INIT_SIZE is enough for most cases, and it seems - * unlikely that the number of entries will reach - * WAIT_EVENT_CUSTOM_HASH_MAX_SIZE. + * The size of the hash table is based on the assumption that usually only a + * handful of entries are needed, but since it's small in absolute terms + * anyway, we leave a generous amount of headroom. */ static HTAB *WaitEventCustomHashByInfo; /* find names from infos */ static HTAB *WaitEventCustomHashByName; /* find infos from names */ -#define WAIT_EVENT_CUSTOM_HASH_INIT_SIZE 16 -#define WAIT_EVENT_CUSTOM_HASH_MAX_SIZE 128 +#define WAIT_EVENT_CUSTOM_HASH_SIZE 128 /* hash table entries */ typedef struct WaitEventCustomEntryByInfo @@ -106,9 +104,9 @@ WaitEventCustomShmemSize(void) Size sz; sz = MAXALIGN(sizeof(WaitEventCustomCounterData)); - sz = add_size(sz, hash_estimate_size(WAIT_EVENT_CUSTOM_HASH_MAX_SIZE, + sz = add_size(sz, hash_estimate_size(WAIT_EVENT_CUSTOM_HASH_SIZE, sizeof(WaitEventCustomEntryByInfo))); - sz = add_size(sz, hash_estimate_size(WAIT_EVENT_CUSTOM_HASH_MAX_SIZE, + sz = add_size(sz, hash_estimate_size(WAIT_EVENT_CUSTOM_HASH_SIZE, sizeof(WaitEventCustomEntryByName))); return sz; } @@ -138,8 +136,7 @@ WaitEventCustomShmemInit(void) info.entrysize = sizeof(WaitEventCustomEntryByInfo); WaitEventCustomHashByInfo = ShmemInitHash("WaitEventCustom hash by wait event information", - WAIT_EVENT_CUSTOM_HASH_INIT_SIZE, - WAIT_EVENT_CUSTOM_HASH_MAX_SIZE, + WAIT_EVENT_CUSTOM_HASH_SIZE, &info, HASH_ELEM | HASH_BLOBS); @@ -148,8 +145,7 @@ WaitEventCustomShmemInit(void) info.entrysize = sizeof(WaitEventCustomEntryByName); WaitEventCustomHashByName = ShmemInitHash("WaitEventCustom hash by name", - WAIT_EVENT_CUSTOM_HASH_INIT_SIZE, - WAIT_EVENT_CUSTOM_HASH_MAX_SIZE, + WAIT_EVENT_CUSTOM_HASH_SIZE, &info, HASH_ELEM | HASH_STRINGS); } @@ -238,7 +234,7 @@ WaitEventCustomNew(uint32 classId, const char *wait_event_name) /* Allocate a new event Id */ SpinLockAcquire(&WaitEventCustomCounter->mutex); - if (WaitEventCustomCounter->nextId >= WAIT_EVENT_CUSTOM_HASH_MAX_SIZE) + if (WaitEventCustomCounter->nextId >= WAIT_EVENT_CUSTOM_HASH_SIZE) { SpinLockRelease(&WaitEventCustomCounter->mutex); ereport(ERROR, diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h index 2a9e9becd26..0ae609aca8b 100644 --- a/src/include/storage/shmem.h +++ b/src/include/storage/shmem.h @@ -32,7 +32,7 @@ extern void InitShmemAllocator(PGShmemHeader *seghdr); extern void *ShmemAlloc(Size size); extern void *ShmemAllocNoError(Size size); extern bool ShmemAddrIsValid(const void *addr); -extern HTAB *ShmemInitHash(const char *name, int64 init_size, int64 max_size, +extern HTAB *ShmemInitHash(const char *name, int64 nelems, HASHCTL *infoP, int hash_flags); extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr); extern Size add_size(Size s1, Size s2);