On Thu, 19 Sept 2024 at 11:54, Masahiko Sawada <sawada.m...@gmail.com> wrote: > I've done some benchmark tests for three different code bases with > different test cases. In short, reducing the generation memory context > block size to 8kB seems to be promising; it mitigates the problem > while keeping a similar performance.
Did you try any sizes between 8KB and 8MB? 1000x reduction seems quite large a jump. There is additional overhead from having more blocks. It means more malloc() work and more free() work when deleting a context. It would be nice to see some numbers with all powers of 2 between 8KB and 8MB. I imagine the returns are diminishing as the block size is reduced further. Another alternative idea would be to defragment transactions with a large number of changes after they grow to some predefined size. Defragmentation would just be a matter of performing palloc/memcpy/pfree for each change. If that's all done at once, all the changes for that transaction would be contiguous in memory. If you're smart about what the trigger point is for performing the defragmentation then I imagine there's not much risk of performance regression for the general case. For example, you might only want to trigger it when MemoryContextMemAllocated() for the generation context exceeds logical_decoding_work_mem by some factor and only do it for transactions where the size of the changes exceeds some threshold. > Overall, reducing the generation context memory block size to 8kB > seems to be promising. And using the bump memory context per > transaction didn't bring performance improvement than I expected in > these cases. Having a bump context per transaction would cause a malloc() every time you create a new context and a free() each time you delete the context when cleaning up the reorder buffer for the transaction. GenerationContext has a "freeblock" field that it'll populate instead of freeing a block. That block will be reused next time a new block is required. For truly FIFO workloads that never need an oversized block, all new blocks will come from the freeblock field once the first block becomes unused. See the comments in GenerationFree(). I expect this is why bump contexts are slower than the generation context for your short transaction workload. David