From 1275bdb6234ebaf6317c3e4d0961961b95d595e2 Mon Sep 17 00:00:00 2001 From: Leonid Yuriev Date: Thu, 6 May 2021 02:05:33 +0300 Subject: [PATCH] mdbx: add `MDBX_opt_merge_threshold_16dot16_percent` option. Change-Id: I416f85096e4b6fd21f2db622f07f31103cb9074a --- mdbx.h | 11 +++++++++++ src/core.c | 37 +++++++++++++++++++++++++++---------- src/internals.h | 6 +++++- 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/mdbx.h b/mdbx.h index 3767f42b..b2f95c8d 100644 --- a/mdbx.h +++ b/mdbx.h @@ -1951,6 +1951,17 @@ enum MDBX_option_t { * Default is 0, i.e. by default no spilling performed during starting nested * transactions, that correspond historically behaviour. */ MDBX_opt_spill_parent4child_denominator, + + /** \brief Controls the in-process threshold of semi-empty pages merge. + * \warning This is experimental option and subject for change or removal. + * \details This option controls the in-process threshold of minimum page + * fill, as used space of percentage of a page. Neighbour pages emptier than + * this value are candidates for merging. The threshold value is specified + * in 1/65536 of percent, which is equivalent to the 16-dot-16 fixed point + * format. The specified value must be in the range from 12.5% (almost empty) + * to 50% (half empty) which corresponds to the range from 8192 and to 32768 + * in units respectively. */ + MDBX_opt_merge_threshold_16dot16_percent, }; #ifndef __cplusplus /** \ingroup c_settings */ diff --git a/src/core.c b/src/core.c index 7f9b08bb..0a2a09dc 100644 --- a/src/core.c +++ b/src/core.c @@ -10779,6 +10779,16 @@ fail: return rc; } +static void recalculate_merge_threshold(MDBX_env *env) { + const unsigned bytes = page_space(env); + env->me_merge_threshold = (uint16_t)( + bytes - (bytes * env->me_options.merge_threshold_16dot16_percent >> 16)); + env->me_merge_threshold_gc = (uint16_t)( + bytes - ((env->me_options.merge_threshold_16dot16_percent > 19005) + ? bytes / 3 /* 33 % */ + : bytes / 4 /* 25 % */)); +} + static void __cold mdbx_setup_pagesize(MDBX_env *env, const size_t pagesize) { STATIC_ASSERT(PTRDIFF_MAX > MAX_MAPSIZE); STATIC_ASSERT(MIN_PAGESIZE > sizeof(MDBX_page) + sizeof(MDBX_meta)); @@ -10811,6 +10821,7 @@ static void __cold mdbx_setup_pagesize(MDBX_env *env, const size_t pagesize) { env->me_psize2log = (uint8_t)log2n_powerof2(pagesize); mdbx_assert(env, pgno2bytes(env, 1) == pagesize); mdbx_assert(env, bytes2pgno(env, pagesize + pagesize) == 2); + recalculate_merge_threshold(env); const pgno_t max_pgno = bytes2pgno(env, MAX_MAPSIZE); if (!env->me_options.flags.non_auto.dp_limit) { @@ -10866,6 +10877,7 @@ __cold int mdbx_env_create(MDBX_env **penv) { env->me_options.spill_min_denominator = 8; env->me_options.spill_parent4child_denominator = 0; env->me_options.dp_loose_limit = 64; + env->me_options.merge_threshold_16dot16_percent = 65536 / 4 /* 25% */; int rc; const size_t os_psize = mdbx_syspagesize(); @@ -16656,16 +16668,10 @@ static int mdbx_rebalance(MDBX_cursor *mc) { STATIC_ASSERT(P_BRANCH == 1); const unsigned minkeys = (pagetype & P_BRANCH) + 1; - /* The threshold of minimum page fill factor, in form of a negative binary - * exponent, i.e. X = 2 means 1/(2**X) == 1/(2**2) == 1/4 == 25%. - * Pages emptier than this are candidates for merging. */ - const unsigned threshold_fill_exp2 = 2; - - /* The threshold of minimum page fill factor, as a number of free bytes on a - * page. Pages emptier than this are candidates for merging. */ - unsigned room_threshold = - page_space(mc->mc_txn->mt_env) - - (page_space(mc->mc_txn->mt_env) >> threshold_fill_exp2); + /* Pages emptier than this are candidates for merging. */ + unsigned room_threshold = likely(mc->mc_dbi != FREE_DBI) + ? mc->mc_txn->mt_env->me_merge_threshold + : mc->mc_txn->mt_env->me_merge_threshold_gc; const MDBX_page *const tp = mc->mc_pg[mc->mc_top]; const unsigned numkeys = page_numkeys(tp); @@ -21681,6 +21687,13 @@ __cold int mdbx_env_set_option(MDBX_env *env, const MDBX_option_t option, env->me_options.dp_loose_limit = (uint8_t)value; break; + case MDBX_opt_merge_threshold_16dot16_percent: + if (unlikely(value < 8192 || value > 32768)) + return MDBX_EINVAL; + env->me_options.merge_threshold_16dot16_percent = (unsigned)value; + recalculate_merge_threshold(env); + break; + default: return MDBX_EINVAL; } @@ -21750,6 +21763,10 @@ __cold int mdbx_env_get_option(const MDBX_env *env, const MDBX_option_t option, *pvalue = env->me_options.dp_loose_limit; break; + case MDBX_opt_merge_threshold_16dot16_percent: + *pvalue = env->me_options.merge_threshold_16dot16_percent; + break; + default: return MDBX_EINVAL; } diff --git a/src/internals.h b/src/internals.h index 609263a1..343ede1d 100644 --- a/src/internals.h +++ b/src/internals.h @@ -1137,7 +1137,10 @@ struct MDBX_env { unsigned me_leaf_nodemax; /* max size of a leaf-node */ uint8_t me_psize2log; /* log2 of DB page size */ int8_t me_stuck_meta; /* recovery-only: target meta page or less that zero */ - unsigned me_os_psize; /* OS page size, from mdbx_syspagesize() */ + uint16_t me_merge_threshold, + me_merge_threshold_gc; /* pages emptier than this are candidates for + merging */ + unsigned me_os_psize; /* OS page size, from mdbx_syspagesize() */ unsigned me_maxreaders; /* size of the reader table */ MDBX_dbi me_maxdbs; /* size of the DB table */ uint32_t me_pid; /* process ID of this env */ @@ -1164,6 +1167,7 @@ struct MDBX_env { uint8_t spill_max_denominator; uint8_t spill_min_denominator; uint8_t spill_parent4child_denominator; + unsigned merge_threshold_16dot16_percent; union { unsigned all; /* tracks options with non-auto values but tuned by user */