]> granicus.if.org Git - postgresql/commitdiff
Avoid statically allocating formatting.c's format string caches.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 16 Oct 2018 17:11:05 +0000 (13:11 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 16 Oct 2018 17:11:09 +0000 (13:11 -0400)
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

index b3ff133027c5a7d7ed5e8c7aabd73e5bfbc8133d..d158ef9adf931847e02d24e30139aa19c92ecda7 100644 (file)
@@ -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"
 
  */
 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);