Rethinking MemoryContext creation

From: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
To: pgsql-hackers(at)lists(dot)postgresql(dot)org
Subject: Rethinking MemoryContext creation
Date: 2017-12-10 01:53:16
Message-ID: 2264.1512870796@sss.pgh.pa.us
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

While fooling around with a different problem, I got annoyed at how slow
MemoryContext creation and deletion is. A big chunk of that seems to be
the palloc/pfree cycle needed to allocate the context header block in
TopMemoryContext. After thinking about it for a bit, I decided that
it'd be smarter to include the context header in the context's first
malloc() allocation, thereby eliminating the palloc/pfree cycle
completely. This is not completely cost-free, because it means that
we must allocate a context's first block ("keeper" block) immediately,
even if no pallocs ever get done in it. However, optimizing for the
unused-context case seems like the wrong thing.

To do this, we need to move the responsibility for allocating and
releasing the context header into the individual context-type-specific
modules, which leads to a certain amount of code duplication, but not a
lot. A big advantage is that we can simplify the overly-complicated
creation API that had the context module calling into mcxt.c which then
recursed back to the context module. Further down the pike there might
be some additional win, since this gives the context module more control
over where to put the header (shared memory maybe?).

Another point worth making is that getting the context headers out of
TopMemoryContext reduces the cost of transiently having a lot of
contexts. Since TopMemoryContext is never reset, and aset.c doesn't
give memory back to libc short of a reset, creating a lot of contexts
permanently bloats TopMemoryContext even if they all get deleted later.

I didn't spend a lot of effort on slab.c or generation.c, just having
them do a separate malloc and free for the context header. If anyone's
really excited about that, they could be improved later.

To cut to the chase: I experimented with simply creating and deleting an
aset.c memory context in a tight loop, optionally with one palloc in the
context before deleting it. On my machine, the time per loop looked like

HEAD with patch

no palloc at all 9.2 us 10.3 us
palloc 100 bytes 18.5 us 11.6 us
palloc 10000 bytes 17.2 us 18.5 us

So the unused-context case does get a bit slower, as we trade off
a malloc/free cycle to save a palloc/pfree cycle. The case where
we've made some use of the context gets considerably quicker though.
Also, the last line shows a situation where the lone palloc request
does not fit in the context's initial allocation so that we're forced
to make two malloc requests not one. This is a bit slower too, but
not by much, and I think it's really an unusual case. (As soon as
you've made any requests that do fit in the initial allocation,
you'd be back to the patch winning.)

Overall I'm seeing about a 5% improvement in a "pgbench -S" scenario,
although that number is a bit shaky since the run-to-run variation
is a few percent anyway.

Thoughts, objections? Anyone want to do some other benchmarking?

regards, tom lane

Attachment Content-Type Size
rethink-memory-context-creation-1.patch text/x-diff 38.5 KB

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Robins Tharakan 2017-12-10 02:51:43 Re: Typo in recent commit
Previous Message Tom Lane 2017-12-10 00:28:28 Re: proposal: alternative psql commands quit and exit