From fd85e9f78d44f0f36776e644380ada077451c992 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 16 Oct 2018 13:11:05 -0400 Subject: [PATCH] Avoid statically allocating formatting.c's format string caches. This eliminates circa 120KB of static data from Postgres' memory footprint. In some usage patterns that space will get allocated anyway, but in many processes it never will be allocated. We can improve matters further by allocating only as many cache entries as we actually use, rather than allocating the whole array on first use. However, to avoid wasting lots of space due to palloc's habit of rounding requests up to power-of-2 sizes, tweak the maximum cacheable format string length to make the struct sizes be powers of 2 or just less. The sizes I chose make the maximums a little bit less than they were before, but I doubt it matters much. While at it, rearrange struct FormatNode to avoid wasting quite so much padding space. This change actually halves the size of that struct on 64-bit machines. Discussion: https://postgr.es/m/20181015200754.7y7zfuzsoux2c4ya@alap3.anarazel.de --- src/backend/utils/adt/formatting.c | 94 ++++++++++++++++++------------ 1 file changed, 56 insertions(+), 38 deletions(-) diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c index b3ff133027..d158ef9adf 100644 --- a/src/backend/utils/adt/formatting.c +++ b/src/backend/utils/adt/formatting.c @@ -93,6 +93,7 @@ #include "utils/float.h" #include "utils/formatting.h" #include "utils/int8.h" +#include "utils/memutils.h" #include "utils/numeric.h" #include "utils/pg_locale.h" @@ -124,10 +125,10 @@ */ typedef struct { - char *name; /* suffix string */ + const char *name; /* suffix string */ int len, /* suffix length */ id, /* used in node->suffix */ - type; /* prefix / postfix */ + type; /* prefix / postfix */ } KeySuffix; /* ---------- @@ -155,10 +156,10 @@ typedef struct typedef struct { - int type; /* NODE_TYPE_XXX, see below */ - const KeyWord *key; /* if type is ACTION */ + uint8 type; /* NODE_TYPE_XXX, see below */ char character[MAX_MULTIBYTE_CHAR_LEN + 1]; /* if type is CHAR */ - int suffix; /* keyword prefix/suffix code, if any */ + uint8 suffix; /* keyword prefix/suffix code, if any */ + const KeyWord *key; /* if type is ACTION */ } FormatNode; #define NODE_TYPE_END 1 @@ -358,14 +359,27 @@ typedef struct * For simplicity, the cache entries are fixed-size, so they allow for the * worst case of a FormatNode for each byte in the picture string. * - * The max number of entries in the caches is DCH_CACHE_ENTRIES + * The CACHE_SIZE constants are computed to make sizeof(DCHCacheEntry) and + * sizeof(NUMCacheEntry) be powers of 2, or just less than that, so that + * we don't waste too much space by palloc'ing them individually. Be sure + * to adjust those macros if you add fields to those structs. + * + * The max number of entries in each cache is DCH_CACHE_ENTRIES * resp. NUM_CACHE_ENTRIES. * ---------- */ -#define NUM_CACHE_SIZE 64 -#define NUM_CACHE_ENTRIES 20 -#define DCH_CACHE_SIZE 128 +#define DCH_CACHE_OVERHEAD \ + MAXALIGN(sizeof(bool) + sizeof(int)) +#define NUM_CACHE_OVERHEAD \ + MAXALIGN(sizeof(bool) + sizeof(int) + sizeof(NUMDesc)) + +#define DCH_CACHE_SIZE \ + ((2048 - DCH_CACHE_OVERHEAD) / (sizeof(FormatNode) + sizeof(char)) - 1) +#define NUM_CACHE_SIZE \ + ((1024 - NUM_CACHE_OVERHEAD) / (sizeof(FormatNode) + sizeof(char)) - 1) + #define DCH_CACHE_ENTRIES 20 +#define NUM_CACHE_ENTRIES 20 typedef struct { @@ -385,12 +399,12 @@ typedef struct } NUMCacheEntry; /* global cache for date/time format pictures */ -static DCHCacheEntry DCHCache[DCH_CACHE_ENTRIES]; +static DCHCacheEntry *DCHCache[DCH_CACHE_ENTRIES]; static int n_DCHCache = 0; /* current number of entries */ static int DCHCounter = 0; /* aging-event counter */ /* global cache for number format pictures */ -static NUMCacheEntry NUMCache[NUM_CACHE_ENTRIES]; +static NUMCacheEntry *NUMCache[NUM_CACHE_ENTRIES]; static int n_NUMCache = 0; /* current number of entries */ static int NUMCounter = 0; /* aging-event counter */ @@ -496,7 +510,7 @@ do { \ *****************************************************************************/ /* ---------- - * Suffixes: + * Suffixes (FormatNode.suffix is an OR of these codes) * ---------- */ #define DCH_S_FM 0x01 @@ -3368,13 +3382,13 @@ DCH_cache_getnew(const char *str) { DCHCacheEntry *ent; - /* counter overflow check - paranoia? */ + /* handle counter overflow by resetting all ages */ if (DCHCounter >= (INT_MAX - DCH_CACHE_ENTRIES)) { DCHCounter = 0; - for (ent = DCHCache; ent < (DCHCache + DCH_CACHE_ENTRIES); ent++) - ent->age = (++DCHCounter); + for (int i = 0; i < n_DCHCache; i++) + DCHCache[i]->age = (++DCHCounter); } /* @@ -3382,15 +3396,16 @@ DCH_cache_getnew(const char *str) */ if (n_DCHCache >= DCH_CACHE_ENTRIES) { - DCHCacheEntry *old = DCHCache + 0; + DCHCacheEntry *old = DCHCache[0]; #ifdef DEBUG_TO_FROM_CHAR elog(DEBUG_elog_output, "cache is full (%d)", n_DCHCache); #endif if (old->valid) { - for (ent = DCHCache + 1; ent < (DCHCache + DCH_CACHE_ENTRIES); ent++) + for (int i = 1; i < DCH_CACHE_ENTRIES; i++) { + ent = DCHCache[i]; if (!ent->valid) { old = ent; @@ -3414,7 +3429,9 @@ DCH_cache_getnew(const char *str) #ifdef DEBUG_TO_FROM_CHAR elog(DEBUG_elog_output, "NEW (%d)", n_DCHCache); #endif - ent = DCHCache + n_DCHCache; + Assert(DCHCache[n_DCHCache] == NULL); + DCHCache[n_DCHCache] = ent = (DCHCacheEntry *) + MemoryContextAllocZero(TopMemoryContext, sizeof(DCHCacheEntry)); ent->valid = false; StrNCpy(ent->str, str, DCH_CACHE_SIZE + 1); ent->age = (++DCHCounter); @@ -3428,20 +3445,19 @@ DCH_cache_getnew(const char *str) static DCHCacheEntry * DCH_cache_search(const char *str) { - int i; - DCHCacheEntry *ent; - - /* counter overflow check - paranoia? */ + /* handle counter overflow by resetting all ages */ if (DCHCounter >= (INT_MAX - DCH_CACHE_ENTRIES)) { DCHCounter = 0; - for (ent = DCHCache; ent < (DCHCache + DCH_CACHE_ENTRIES); ent++) - ent->age = (++DCHCounter); + for (int i = 0; i < n_DCHCache; i++) + DCHCache[i]->age = (++DCHCounter); } - for (i = 0, ent = DCHCache; i < n_DCHCache; i++, ent++) + for (int i = 0; i < n_DCHCache; i++) { + DCHCacheEntry *ent = DCHCache[i]; + if (ent->valid && strcmp(ent->str, str) == 0) { ent->age = (++DCHCounter); @@ -4047,13 +4063,13 @@ NUM_cache_getnew(const char *str) { NUMCacheEntry *ent; - /* counter overflow check - paranoia? */ + /* handle counter overflow by resetting all ages */ if (NUMCounter >= (INT_MAX - NUM_CACHE_ENTRIES)) { NUMCounter = 0; - for (ent = NUMCache; ent < (NUMCache + NUM_CACHE_ENTRIES); ent++) - ent->age = (++NUMCounter); + for (int i = 0; i < n_NUMCache; i++) + NUMCache[i]->age = (++NUMCounter); } /* @@ -4061,15 +4077,16 @@ NUM_cache_getnew(const char *str) */ if (n_NUMCache >= NUM_CACHE_ENTRIES) { - NUMCacheEntry *old = NUMCache + 0; + NUMCacheEntry *old = NUMCache[0]; #ifdef DEBUG_TO_FROM_CHAR elog(DEBUG_elog_output, "Cache is full (%d)", n_NUMCache); #endif if (old->valid) { - for (ent = NUMCache + 1; ent < (NUMCache + NUM_CACHE_ENTRIES); ent++) + for (int i = 1; i < NUM_CACHE_ENTRIES; i++) { + ent = NUMCache[i]; if (!ent->valid) { old = ent; @@ -4093,7 +4110,9 @@ NUM_cache_getnew(const char *str) #ifdef DEBUG_TO_FROM_CHAR elog(DEBUG_elog_output, "NEW (%d)", n_NUMCache); #endif - ent = NUMCache + n_NUMCache; + Assert(NUMCache[n_NUMCache] == NULL); + NUMCache[n_NUMCache] = ent = (NUMCacheEntry *) + MemoryContextAllocZero(TopMemoryContext, sizeof(NUMCacheEntry)); ent->valid = false; StrNCpy(ent->str, str, NUM_CACHE_SIZE + 1); ent->age = (++NUMCounter); @@ -4107,20 +4126,19 @@ NUM_cache_getnew(const char *str) static NUMCacheEntry * NUM_cache_search(const char *str) { - int i; - NUMCacheEntry *ent; - - /* counter overflow check - paranoia? */ + /* handle counter overflow by resetting all ages */ if (NUMCounter >= (INT_MAX - NUM_CACHE_ENTRIES)) { NUMCounter = 0; - for (ent = NUMCache; ent < (NUMCache + NUM_CACHE_ENTRIES); ent++) - ent->age = (++NUMCounter); + for (int i = 0; i < n_NUMCache; i++) + NUMCache[i]->age = (++NUMCounter); } - for (i = 0, ent = NUMCache; i < n_NUMCache; i++, ent++) + for (int i = 0; i < n_NUMCache; i++) { + NUMCacheEntry *ent = NUMCache[i]; + if (ent->valid && strcmp(ent->str, str) == 0) { ent->age = (++NUMCounter);