On Wed, Jul 24, 2024 at 2:42 PM John Naylor <johncnaylo...@gmail.com> wrote:
> As for lowering the limit, we've experimented with 256kB here:
>
> https://www.postgresql.org/message-id/canwcazzutvz3lsypauyqvzcezxz7qe+9ntnhgyzdtwxpul+...@mail.gmail.com
>
> As I mention there, going lower than that would need a small amount of
> reorganization in the radix tree. Not difficult -- the thing I'm
> concerned about is that we'd likely need to document a separate
> minimum for DSA, since that behaves strangely with 256kB and might not
> work at all lower than that.

For experimentation, here's a rough patch (really two, squashed
together for now) that allows m_w_m to go down to 64kB.

drop table if exists test;
create table test (a int) with (autovacuum_enabled=false, fillfactor=10);
insert into test (a) select i from generate_series(1,2000) i;
create index on test (a);
update test set a = a + 1;

set maintenance_work_mem = '64kB';
vacuum (verbose) test;

INFO:  vacuuming "john.public.test"
INFO:  finished vacuuming "john.public.test": index scans: 3
pages: 0 removed, 91 remain, 91 scanned (100.00% of total)

The advantage with this is that we don't need to care about
MEMORY_CONTEXT_CHECKING or 32/64 bit-ness, since allocating a single
large node will immediately blow the limit, and that will happen
fairly quickly regardless. I suspect going this low will not work with
dynamic shared memory and if so would need a warning comment.
From 25ccfb9ed812c72194bd585906f9f11e18400422 Mon Sep 17 00:00:00 2001
From: John Naylor <john.nay...@postgresql.org>
Date: Wed, 24 Jul 2024 18:41:51 +0700
Subject: [PATCH v1] WIP: set minimum maintenance_work_mem to 64kB, matching
 work_mem

To allow this to work for vacuum, create radix tree node
contexts lazily.
---
 src/backend/utils/misc/guc_tables.c |  2 +-
 src/include/lib/radixtree.h         | 44 +++++++++++++++++++++--------
 2 files changed, 34 insertions(+), 12 deletions(-)

diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index f6fcdebb03..853ad8dba1 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -2445,7 +2445,7 @@ struct config_int ConfigureNamesInt[] =
 			GUC_UNIT_KB
 		},
 		&maintenance_work_mem,
-		65536, 1024, MAX_KILOBYTES,
+		65536, 64, MAX_KILOBYTES,
 		NULL, NULL, NULL
 	},
 
diff --git a/src/include/lib/radixtree.h b/src/include/lib/radixtree.h
index 88bf695e3f..bd83b670dd 100644
--- a/src/include/lib/radixtree.h
+++ b/src/include/lib/radixtree.h
@@ -834,12 +834,30 @@ RT_ALLOC_NODE(RT_RADIX_TREE * tree, const uint8 kind, const RT_SIZE_CLASS size_c
 	RT_CHILD_PTR allocnode;
 	RT_NODE    *node;
 	size_t		allocsize;
+	RT_SIZE_CLASS_ELEM class_info;
 
-	allocsize = RT_SIZE_CLASS_INFO[size_class].allocsize;
+	class_info = RT_SIZE_CLASS_INFO[size_class];
+	allocsize = class_info.allocsize;
 
 #ifdef RT_SHMEM
 	allocnode.alloc = dsa_allocate(tree->dsa, allocsize);
 #else
+
+	/*
+	 * The context for RT_CLASS_4 should have been created when the tree was
+	 * initialized.
+	 */
+	Assert(tree->node_slabs[RT_CLASS_4] != NULL);
+		if (size_class > RT_CLASS_4 && tree->node_slabs[size_class] == NULL)
+	{
+		size_t		blocksize = RT_SLAB_BLOCK_SIZE(allocsize);
+
+		tree->node_slabs[size_class] = SlabContextCreate(tree->context,
+														 class_info.name,
+														 blocksize,
+														 allocsize);
+	}
+
 	allocnode.alloc = (RT_PTR_ALLOC) MemoryContextAlloc(tree->node_slabs[size_class],
 														allocsize);
 #endif
@@ -1827,6 +1845,9 @@ RT_CREATE(MemoryContext ctx)
 	RT_CHILD_PTR rootnode;
 #ifdef RT_SHMEM
 	dsa_pointer dp;
+#else
+	RT_SIZE_CLASS_ELEM class_info;
+	size_t		node_blocksize;
 #endif
 
 	old_ctx = MemoryContextSwitchTo(ctx);
@@ -1852,17 +1873,18 @@ RT_CREATE(MemoryContext ctx)
 #else
 	tree->ctl = (RT_RADIX_TREE_CONTROL *) palloc0(sizeof(RT_RADIX_TREE_CONTROL));
 
-	/* Create a slab context for each size class */
-	for (int i = 0; i < RT_NUM_SIZE_CLASSES; i++)
-	{
-		RT_SIZE_CLASS_ELEM size_class = RT_SIZE_CLASS_INFO[i];
-		size_t		inner_blocksize = RT_SLAB_BLOCK_SIZE(size_class.allocsize);
+	/*
+	 * For now only create a slab context for the smallest size class. The
+	 * rest will be created on demand. This allows us to stay within memory
+	 * settings that are smaller than the size of the larger slab blocks.
+	 */
+	class_info = RT_SIZE_CLASS_INFO[RT_CLASS_4];
+	node_blocksize = RT_SLAB_BLOCK_SIZE(class_info.allocsize);
 
-		tree->node_slabs[i] = SlabContextCreate(ctx,
-												size_class.name,
-												inner_blocksize,
-												size_class.allocsize);
-	}
+	tree->node_slabs[RT_CLASS_4] = SlabContextCreate(ctx,
+													 class_info.name,
+													 node_blocksize,
+													 class_info.allocsize);
 
 	/* By default we use the passed context for leaves. */
 	tree->leaf_context = tree->context;
-- 
2.45.2

Reply via email to