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 <tomas@vondra.me>
Discussion: https://www.postgresql.org/message-id/01ab1d41-3eda-4705-8bbd-af898f5007f1@iki.fi
This commit is contained in:
Heikki Linnakangas 2026-04-04 02:40:20 +03:00
parent d438a36591
commit 9ebe1c4f2c
7 changed files with 16 additions and 33 deletions

View file

@ -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);

View file

@ -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);
}

View file

@ -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);
}
/*

View file

@ -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 |

View file

@ -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 |

View file

@ -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,

View file

@ -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);