From 939598de12ebbe94532e662b95fec5654483eebb Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Mon, 30 Mar 2026 18:21:01 +0300 Subject: [PATCH v2 4/8] 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, most callers already used the same size for both. Allowing the table to grow much beyond the initial size is bad for performance anyway as we don't support resizing the hash table's dictionary part. 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 | 7 +----- src/backend/storage/lmgr/predicate.c | 3 --- src/backend/utils/activity/wait_event.c | 22 ++++++++----------- src/include/storage/shmem.h | 2 +- 7 files changed, 17 insertions(+), 37 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 a6fd4c30059..fd2c7c4590a 100644 --- a/src/backend/storage/ipc/shmem.c +++ b/src/backend/storage/ipc/shmem.c @@ -313,14 +313,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 @@ -333,8 +326,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 */ { @@ -348,7 +340,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; @@ -368,7 +360,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 234643e4dd7..ae524bb02d6 100644 --- a/src/backend/storage/lmgr/lock.c +++ b/src/backend/storage/lmgr/lock.c @@ -445,8 +445,7 @@ void LockManagerShmemInit(void) { HASHCTL info; - int64 init_table_size, - max_table_size; + int64 max_table_size; bool found; /* @@ -454,7 +453,6 @@ LockManagerShmemInit(void) * calculations must agree with LockManagerShmemSize! */ max_table_size = NLOCKENTS(); - init_table_size = max_table_size / 2; /* * Allocate hash table for LOCK structs. This stores per-locked-object @@ -465,14 +463,12 @@ LockManagerShmemInit(void) info.num_partitions = NUM_LOCK_PARTITIONS; LockMethodLockHash = ShmemInitHash("LOCK hash", - init_table_size, max_table_size, &info, HASH_ELEM | HASH_BLOBS | HASH_PARTITION); /* Assume an average of 2 holders per lock */ max_table_size *= 2; - init_table_size *= 2; /* * Allocate hash table for PROCLOCK structs. This stores @@ -484,7 +480,6 @@ LockManagerShmemInit(void) info.num_partitions = NUM_LOCK_PARTITIONS; LockMethodProcLockHash = ShmemInitHash("PROCLOCK hash", - init_table_size, max_table_size, &info, HASH_ELEM | HASH_FUNCTION | HASH_PARTITION); diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c index ae0e96aee5f..4c559b2d3ee 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); -- 2.47.3