From 11fda58f829c03d2a7c6476affc61862a078f741 Mon Sep 17 00:00:00 2001 From: Masahiko Sawada Date: Mon, 17 Apr 2023 22:27:04 +0900 Subject: [PATCH v32 17/18] tidstore, vacuum: Specify the init and max DSA segment size based on m_w_m --- src/backend/access/common/tidstore.c | 32 +++++---------------------- src/backend/commands/vacuumparallel.c | 21 ++++++++++++++---- 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/src/backend/access/common/tidstore.c b/src/backend/access/common/tidstore.c index 9360520482..571d15c5c3 100644 --- a/src/backend/access/common/tidstore.c +++ b/src/backend/access/common/tidstore.c @@ -180,39 +180,15 @@ TidStoreCreate(size_t max_bytes, int max_off, dsa_area *area) ts = palloc0(sizeof(TidStore)); - /* - * Create the radix tree for the main storage. - * - * Memory consumption depends on the number of stored tids, but also on the - * distribution of them, how the radix tree stores, and the memory management - * that backed the radix tree. The maximum bytes that a TidStore can - * use is specified by the max_bytes in TidStoreCreate(). We want the total - * amount of memory consumption by a TidStore not to exceed the max_bytes. - * - * In local TidStore cases, the radix tree uses slab allocators for each kind - * of node class. The most memory consuming case while adding Tids associated - * with one page (i.e. during TidStoreSetBlockOffsets()) is that we allocate a new - * slab block for a new radix tree node, which is approximately 70kB. Therefore, - * we deduct 70kB from the max_bytes. - * - * In shared cases, DSA allocates the memory segments big enough to follow - * a geometric series that approximately doubles the total DSA size (see - * make_new_segment() in dsa.c). We simulated the how DSA increases segment - * size and the simulation revealed, the 75% threshold for the maximum bytes - * perfectly works in case where the max_bytes is a power-of-2, and the 60% - * threshold works for other cases. - */ if (area != NULL) { dsa_pointer dp; - float ratio = ((max_bytes & (max_bytes - 1)) == 0) ? 0.75 : 0.6; ts->tree.shared = shared_rt_create(CurrentMemoryContext, area, LWTRANCHE_SHARED_TIDSTORE); dp = dsa_allocate0(area, sizeof(TidStoreControl)); ts->control = (TidStoreControl *) dsa_get_address(area, dp); - ts->control->max_bytes = (size_t) (max_bytes * ratio); ts->area = area; ts->control->magic = TIDSTORE_MAGIC; @@ -223,11 +199,15 @@ TidStoreCreate(size_t max_bytes, int max_off, dsa_area *area) else { ts->tree.local = local_rt_create(CurrentMemoryContext); - ts->control = (TidStoreControl *) palloc0(sizeof(TidStoreControl)); - ts->control->max_bytes = max_bytes - (70 * 1024); } + /* + * max_bytes is forced to be at least 64kB, the current minimum valid value + * for the work_mem GUC. + */ + ts->control->max_bytes = Max(64 * 1024L, max_bytes); + ts->control->max_off = max_off; ts->control->max_off_nbits = pg_ceil_log2_32(max_off); diff --git a/src/backend/commands/vacuumparallel.c b/src/backend/commands/vacuumparallel.c index 8385d375db..17699aa007 100644 --- a/src/backend/commands/vacuumparallel.c +++ b/src/backend/commands/vacuumparallel.c @@ -252,6 +252,8 @@ parallel_vacuum_init(Relation rel, Relation *indrels, int nindexes, Size est_indstats_len; Size est_shared_len; Size dsa_minsize = dsa_minimum_size(); + Size init_segsize; + Size max_segsize; int nindexes_mwm = 0; int parallel_workers = 0; int querylen; @@ -367,12 +369,23 @@ parallel_vacuum_init(Relation rel, Relation *indrels, int nindexes, shm_toc_insert(pcxt->toc, PARALLEL_VACUUM_KEY_INDEX_STATS, indstats); pvs->indstats = indstats; - /* Prepare DSA space for dead items */ + /* + * Prepare DSA space for dead items. + * + * Since total DSA size grows while following a geometric series by default, + * we specify both the initial DSA segment and maximum DSA segment sizes + * based on the memory available for parallel vacuum. Typically, the initial + * segment size is 1MB and the maximum segment size is vac_work_mem / 8, and + * heap scan stops after allocating 1.125 times more memory than vac_work_mem. + */ + init_segsize = Min(vac_work_mem / 4, (1024 * 1024)); + max_segsize = Max(vac_work_mem / 8, (8 * 1024 * 1024)); area_space = shm_toc_allocate(pcxt->toc, dsa_minsize); shm_toc_insert(pcxt->toc, PARALLEL_VACUUM_KEY_DEAD_ITEMS, area_space); - dead_items_dsa = dsa_create_in_place(area_space, dsa_minsize, - LWTRANCHE_PARALLEL_VACUUM_DSA, - pcxt->seg); + dead_items_dsa = dsa_create_in_place_extended(area_space, dsa_minsize, + LWTRANCHE_PARALLEL_VACUUM_DSA, + pcxt->seg, + init_segsize, max_segsize); dead_items = TidStoreCreate(vac_work_mem, max_offset, dead_items_dsa); pvs->dead_items = dead_items; pvs->dead_items_area = dead_items_dsa; -- 2.31.1