Re: Separate memory contexts for relcache and catcache - Mailing list pgsql-hackers

From Andres Freund
Subject Re: Separate memory contexts for relcache and catcache
Date
Msg-id dywwv6v6vq3wfqyebypspq7kuez44tnycbvqjspgsqypuunbzn@mzixkn6g47y2
Whole thread Raw
In response to Re: Separate memory contexts for relcache and catcache  (Jeff Davis <pgsql@j-davis.com>)
Responses Re: Separate memory contexts for relcache and catcache
List pgsql-hackers
Hi,

On 2024-11-01 14:47:37 -0700, Jeff Davis wrote:
> On Fri, 2024-11-01 at 15:19 -0400, Andres Freund wrote:
> > I'm a bit worried about the increase in "wasted" memory we might end
> > up when
> > creating one aset for *everything*. Just splitting out Relcache and
> > CatCache
> > isn't a big deal from that angle, they're always used reasonably
> > much. But
> > creating a bunch of barely used contexts does have the potential for
> > lots of
> > memory being wasted at the end of a page and on freelists.  It might
> > be ok as
> > far was what you proposed in the above email, I haven't analyzed that
> > in depth
> > yet.
>
> Melih raised similar concerns. The new contexts that my patch created
> were CatCacheContext, RelCacheContext, SPICacheContext,
> PgOutputContext, PlanCacheContext, TextSearchCacheContext, and
> TypCacheContext.
>
> Those are all created lazily, so you need to at least be using the
> relevant feature before it has any cost (with the exception of the
> first two).

Well, you can't get very far without using at least CatCacheContext,
RelCacheContext, PlanCacheContext, TypCacheContext. The others are indeed much
more specific and not really worth worrying about.


> > > I agree with others that we should look at changing the initial
> > > size or
> > > type of the contexts, but that should be a separate commit.
> >
> > It needs to be done close together though, otherwise we'll increase
> > the
> > new-connection-memory-usage of postgres measurably.
>
> I don't have a strong opinion here; that was a passing comment. But I'm
> curious: why it would increase the per-connection memory usage much to
> just have a couple new memory contexts?

"much" is maybe too strong. But the memory usage in a new connection is fairly
low, it doesn't take a large increase to be noticeable percentage-wise. And
given how much people love having poolers full of idle connections, it shows
up in aggregate.


> > I've previously proposed creating a type of memory context that's
> > intended for
> > places where we never expect to allocate much which allocates from
> > either a
> > superior memory context or just from the system allocator and tracks
> > memory
> > via linked lists.
>
> Why not just use ALLOCSET_SMALL_SIZES?

That helps some, but not *that* much. You still end up with a bunch of partially
filled blocks. Here's e.g. an excerpt with your patch applied:

│             name             │                     ident                      │   type   │ level │     path      │
total_bytes│ total_nblocks │ free_bytes │ free_chunks │ used_bytes │
 

├──────────────────────────────┼────────────────────────────────────────────────┼──────────┼───────┼───────────────┼─────────────┼───────────────┼────────────┼─────────────┼────────────┤
│ CacheMemoryContext           │ (null)                                         │ AllocSet │     2 │ {1,19}        │
   8192 │             1 │       7952 │           0 │        240 │
 
│ TypCacheContext              │ (null)                                         │ AllocSet │     3 │ {1,19,28}     │
   8192 │             1 │       4816 │           0 │       3376 │
 
│ search_path processing cache │ (null)                                         │ AllocSet │     3 │ {1,19,29}     │
   8192 │             1 │       5280 │           7 │       2912 │
 
│ CatCacheContext              │ (null)                                         │ AllocSet │     3 │ {1,19,30}     │
 262144 │             6 │      14808 │           0 │     247336 │
 
│ RelCacheContext              │ (null)                                         │ AllocSet │     3 │ {1,19,31}     │
 262144 │             6 │       8392 │           2 │     253752 │
 
│ relation rules               │ pg_backend_memory_contexts                     │ AllocSet │     4 │ {1,19,31,34}  │
   8192 │             4 │       3280 │           1 │       4912 │
 
│ index info                   │ manyrows_pkey                                  │ AllocSet │     4 │ {1,19,31,35}  │
   2048 │             2 │        864 │           1 │       1184 │
 
│ index info                   │ pg_statistic_ext_relid_index                   │ AllocSet │     4 │ {1,19,31,36}  │
   2048 │             2 │        928 │           1 │       1120 │
 
│ index info                   │ pg_class_tblspc_relfilenode_index              │ AllocSet │     4 │ {1,19,31,37}  │
   2048 │             2 │        440 │           1 │       1608 │
 

(this is a tiny bit misleading as "search_path processing cache" was just moved")

You can quickly see that the various contexts have a decent amount of free
space, some of their space.

We've already been more aggressive about using separate contets for indexes -
and in aggregate that memory usage shows up:

postgres[1088243][1]=# SELECT count(*), sum(total_bytes) as total_bytes, sum(total_nblocks) as total_nblocks,
sum(free_bytes)free_bytes, sum(free_chunks) as free_chunks, sum(used_bytes) used_bytes FROM pg_backend_memory_contexts
WHEREpath @> (SELECT path FROM pg_backend_memory_contexts WHERE name = 'CacheMemoryContext') and name = 'index info'
 
┌───────┬─────────────┬───────────────┬────────────┬─────────────┬────────────┐
│ count │ total_bytes │ total_nblocks │ free_bytes │ free_chunks │ used_bytes │
├───────┼─────────────┼───────────────┼────────────┼─────────────┼────────────┤
│    87 │      162816 │           144 │      48736 │         120 │     114080 │
└───────┴─────────────┴───────────────┴────────────┴─────────────┴────────────┘



And it's not just the partially filled blocks that are an "issue", it's also
the freelists that are much less likely to be used soon if they're split very
granularly. Often we'll end up with memory in freelists that are created while
building some information that then will not be used again.


Without your patch:

┌────────────────────┬────────────────────────────────────────────────┬──────────┬───────┬────────────┬─────────────┬───────────────┬────────────┬─────────────┬────────────┐
│        name        │                     ident                      │   type   │ level │    path    │ total_bytes │
total_nblocks│ free_bytes │ free_chunks │ used_bytes │
 

├────────────────────┼────────────────────────────────────────────────┼──────────┼───────┼────────────┼─────────────┼───────────────┼────────────┼─────────────┼────────────┤
│ CacheMemoryContext │ (null)                                         │ AllocSet │     2 │ {1,17}     │      524288 │
         7 │      75448 │           0 │     448840 │
 
│ relation rules     │ pg_backend_memory_contexts                     │ AllocSet │     3 │ {1,17,27}  │        8192 │
         4 │       3472 │           4 │       4720 │
 
...


Greetings,

Andres Freund



pgsql-hackers by date:

Previous
From: Tom Lane
Date:
Subject: Re: Using Expanded Objects other than Arrays from plpgsql
Next
From: Michel Pelletier
Date:
Subject: Re: Using Expanded Objects other than Arrays from plpgsql