Re: Reduce memory overheads for storing a Memoize tuple

From: Chao Li <li(dot)evan(dot)chao(at)gmail(dot)com>
To: David Rowley <dgrowleyml(at)gmail(dot)com>
Cc: PostgreSQL Developers <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Subject: Re: Reduce memory overheads for storing a Memoize tuple
Date: 2026-08-03 04:52:01
Message-ID: 0BD5FF1C-EE3E-44F1-B48C-51EAD8496890@gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

> On Aug 1, 2026, at 16:35, David Rowley <dgrowleyml(at)gmail(dot)com> wrote:
>
> a0942f441 added ExecCopySlotMinimalTupleExtra(), which accepts a
> parameter to specify the number of "extra" bytes that we want to
> allocate along with the MinimalTuple. That's now used in
> execGrouping.c to save some memory.
>
> The same optimisation can be applied in nodeMemoize.c. MemoizeTuple
> has a field for the tuple being stored and 1 other field to point to
> the next tuple cached for this MemoizeEntry. Here we could use
> ExecCopySlotMinimalTupleExtra() to specify that we want a pointer's
> worth of extra bytes palloc'd for the MinimalTuple, and then store the
> pointer to the next tuple in those bytes. This saves 16 bytes per
> cached tuple. 24 bytes less because we don't palloc a MemoizeTuple
> (including the MemoryChunk's 8 bytes), and 8 bytes more for the
> ExecCopySlotMinimalTupleExtra bytes, a net saving of 16 bytes per
> tuple.
>
> Making Memoize use less memory is useful in cases where the cache
> would otherwise have to reload entries that were cached previously but
> were evicted due to reaching memory limits.
>
> A quick example:
>
> create table t1 (a int not null);
> create table t2 (a int not null);
> insert into t1 select x from generate_series(1,100000) x,
> generate_series(1,100);
> create index on t1 (a);
> insert into t2 select x%1000+1 from generate_series(1,1000000)x;
> analyze t1,t2;
>
> explain analyze select count(*) from t1 inner join t2 on t1.a=t2.a;
>
> Master: Memory Usage: 3583kB
> Patched: Memory Usage: 2801kB
>
> Really, the savings are double what's reported by EXPLAIN ANALYZE, as
> CACHE_TUPLE_BYTES doesn't account for any of the MemoryChunks that are
> consumed by palloc. We're now doing 1 fewer palloc per tuple due to
> the removal of the palloc_object(MemoizeTuple) code, so more like 30%
> less memory for this case.
>
> Patch attached.
>
> David
> <v1-0001-Reduce-memory-overheads-for-storing-a-Memoize-tup.patch>

The optimized data structure looks good to me. The test result on my side exactly matches yours: 3583kB vs. 2801kB. I had to turn off hash join, otherwise the planner always chose a parallel hash join.

I have only one nitpick. MAXALIGN(sizeof(MinimalTuple)) appears 4 times. Would it make sense to define a macro for it, say MEMOIZE_TUPLE_LINK_SIZE?

Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Ayush Tiwari 2026-08-03 05:10:49 Re: Avoid unnecessary server restarts in the Kerberos TAP test
Previous Message Michael Paquier 2026-08-03 04:51:17 Re: Avoid unnecessary server restarts in the Kerberos TAP test