From 5792eb31eb868498fe817358ba91895c96a2cfc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9B=D0=B5=D0=BE=D0=BD=D0=B8=D0=B4=20=D0=AE=D1=80=D1=8C?= =?UTF-8?q?=D0=B5=D0=B2=20=28Leonid=20Yuriev=29?= Date: Mon, 20 May 2024 14:36:50 +0300 Subject: [PATCH] =?UTF-8?q?mdbx:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BE=D0=BF=D1=86=D0=B8=D0=B9=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20subpage:=20limit,=20room=5Fthreshold,=20re?= =?UTF-8?q?serve=5Fprereq,=20reserve=5Flimit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mdbx.h | 44 +++++++++++++++++++++++- src/env-opts.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++ src/env.c | 10 +----- src/internals.h | 7 ++++ src/page-ops.c | 31 ++++++++++++++--- src/page-ops.h | 2 +- src/proto.h | 1 + 7 files changed, 169 insertions(+), 15 deletions(-) diff --git a/mdbx.h b/mdbx.h index 37cfd10f..7a7b25e0 100644 --- a/mdbx.h +++ b/mdbx.h @@ -2344,7 +2344,49 @@ typedef enum MDBX_option { * будет УМЕНЬШАТЬ неравномерность заполнения страниц. * * \see MDBX_opt_merge_threshold_16dot16_percent */ - MDBX_opt_prefer_waf_insteadof_balance + MDBX_opt_prefer_waf_insteadof_balance, + + /** \brief Задаёт в % максимальный размер вложенных страниц, используемых для + * размещения небольшого количества мульти-значений связанных с одном ключем. + * + * Использование вложенных страниц, вместо выноса значений на отдельные + * страницы вложенного дерева, позволяет уменьшить объем неиспользуемого места + * и этим увеличить плотность размещения данных. + * + * Но с увеличением размера вложенных страниц требуется больше листовых + * страниц основного дерева, что также увеличивает высоту основного дерева. + * Кроме этого, изменение данных на вложенных страницах требует дополнительных + * копирований, поэтому стоимость может быть больше во многих сценариях. + * + * min 12.5% (8192), max 100% (65535), default = 100% */ + MDBX_opt_subpage_limit, + + /** \brief Задаёт в % минимальный объём свободного места на основной странице, + * при отсутствии которого вложенные страницы выносятся в отдельное дерево. + * + * min 0, max 100% (65535), default = 0 */ + MDBX_opt_subpage_room_threshold, + + /** \brief Задаёт в % минимальный объём свободного места на основной странице, + * при наличии которого, производится резервирование места во вложенной. + * + * Если на основной странице свободного места недостаточно, то вложенная + * страница будет минимального размера. В свою очередь, при отсутствии резерва + * во вложенной странице, каждое добавлении в неё элементов будет требовать + * переформирования основной страниц с переносом всех узлов данных. + * + * Поэтому резервирование места, как правило, выгодно в сценариях с + * интенсивным добавлением коротких мульти-значений, например при + * индексировании. Но уменьшает плотность размещения данных, соответственно + * увеличивает объем БД и операций ввода-вывода. + * + * min 0, max 100% (65535), default = 42% (27525) */ + MDBX_opt_subpage_reserve_prereq, + + /** \brief Задаёт в % ограничение резервирования места на вложенных страницах. + * + * min 0, max 100% (65535), default = 4.2% (2753) */ + MDBX_opt_subpage_reserve_limit } MDBX_option_t; /** \brief Sets the value of a extra runtime options for an environment. diff --git a/src/env-opts.c b/src/env-opts.c index c1e6324d..659fb5fa 100644 --- a/src/env-opts.c +++ b/src/env-opts.c @@ -31,6 +31,26 @@ static bool default_prefer_waf_insteadof_balance(const MDBX_env *env) { return false; } +static uint16_t default_subpage_limit(const MDBX_env *env) { + (void)env; + return 65535 /* 100% */; +} + +static uint16_t default_subpage_room_threshold(const MDBX_env *env) { + (void)env; + return 0 /* 0% */; +} + +static uint16_t default_subpage_reserve_prereq(const MDBX_env *env) { + (void)env; + return 27525 /* 42% */; +} + +static uint16_t default_subpage_reserve_limit(const MDBX_env *env) { + (void)env; + return 2753 /* 4.2% */; +} + void env_options_init(MDBX_env *env) { env->options.rp_augment_limit = MDBX_PNL_INITIAL; env->options.dp_reserve_limit = MDBX_PNL_INITIAL; @@ -50,6 +70,11 @@ void env_options_init(MDBX_env *env) { #endif /* Linux */ MDBX_WRITETHROUGH_THRESHOLD_DEFAULT; #endif /* Windows */ + + env->options.subpage.limit = default_subpage_limit(env); + env->options.subpage.room_threshold = default_subpage_room_threshold(env); + env->options.subpage.reserve_prereq = default_subpage_reserve_prereq(env); + env->options.subpage.reserve_limit = default_subpage_reserve_limit(env); } void env_options_adjust_defaults(MDBX_env *env) { @@ -318,6 +343,54 @@ __cold int mdbx_env_set_option(MDBX_env *env, const MDBX_option_t option, env->options.prefer_waf_insteadof_balance = value != 0; break; + case MDBX_opt_subpage_limit: + if (value == /* default */ UINT64_MAX) { + env->options.subpage.limit = default_subpage_limit(env); + recalculate_subpage_thresholds(env); + } else if (value > 65535) + err = MDBX_EINVAL; + else { + env->options.subpage.limit = (uint16_t)value; + recalculate_subpage_thresholds(env); + } + break; + + case MDBX_opt_subpage_room_threshold: + if (value == /* default */ UINT64_MAX) { + env->options.subpage.room_threshold = default_subpage_room_threshold(env); + recalculate_subpage_thresholds(env); + } else if (value > 65535) + err = MDBX_EINVAL; + else { + env->options.subpage.room_threshold = (uint16_t)value; + recalculate_subpage_thresholds(env); + } + break; + + case MDBX_opt_subpage_reserve_prereq: + if (value == /* default */ UINT64_MAX) { + env->options.subpage.reserve_prereq = default_subpage_reserve_prereq(env); + recalculate_subpage_thresholds(env); + } else if (value > 65535) + err = MDBX_EINVAL; + else { + env->options.subpage.reserve_prereq = (uint16_t)value; + recalculate_subpage_thresholds(env); + } + break; + + case MDBX_opt_subpage_reserve_limit: + if (value == /* default */ UINT64_MAX) { + env->options.subpage.reserve_limit = default_subpage_reserve_limit(env); + recalculate_subpage_thresholds(env); + } else if (value > 65535) + err = MDBX_EINVAL; + else { + env->options.subpage.reserve_limit = (uint16_t)value; + recalculate_subpage_thresholds(env); + } + break; + default: return MDBX_EINVAL; } @@ -411,6 +484,22 @@ __cold int mdbx_env_get_option(const MDBX_env *env, const MDBX_option_t option, *pvalue = env->options.prefer_waf_insteadof_balance; break; + case MDBX_opt_subpage_limit: + *pvalue = env->options.subpage.limit; + break; + + case MDBX_opt_subpage_room_threshold: + *pvalue = env->options.subpage.room_threshold; + break; + + case MDBX_opt_subpage_reserve_prereq: + *pvalue = env->options.subpage.reserve_prereq; + break; + + case MDBX_opt_subpage_reserve_limit: + *pvalue = env->options.subpage.reserve_limit; + break; + default: return MDBX_EINVAL; } diff --git a/src/env.c b/src/env.c index 10fcfc29..2d5dadc5 100644 --- a/src/env.c +++ b/src/env.c @@ -58,15 +58,7 @@ __cold unsigned env_setup_pagesize(MDBX_env *env, const size_t pagesize) { eASSERT(env, pgno2bytes(env, 1) == pagesize); eASSERT(env, bytes2pgno(env, pagesize + pagesize) == 2); recalculate_merge_thresholds(env); - - /* TODO: recalculate me_subpage_xyz values from MDBX_opt_subpage_xyz. */ - env->subpage_limit = env->leaf_nodemax - NODESIZE; - env->subpage_room_threshold = 0; - env->subpage_reserve_prereq = env->leaf_nodemax; - env->subpage_reserve_limit = env->subpage_limit / 42; - eASSERT(env, env->subpage_reserve_prereq > - env->subpage_room_threshold + env->subpage_reserve_limit); - eASSERT(env, env->leaf_nodemax >= env->subpage_limit + NODESIZE); + recalculate_subpage_thresholds(env); const pgno_t max_pgno = bytes2pgno(env, MAX_MAPSIZE); if (!env->options.flags.non_auto.dp_limit) { diff --git a/src/internals.h b/src/internals.h index d3c36bc9..e986e6c5 100644 --- a/src/internals.h +++ b/src/internals.h @@ -408,6 +408,13 @@ struct MDBX_env { bool prefault_write; bool prefer_waf_insteadof_balance; /* Strive to minimize WAF instead of balancing pages fullment */ + struct { + uint16_t limit; + uint16_t room_threshold; + uint16_t reserve_prereq; + uint16_t reserve_limit; + } subpage; + union { unsigned all; /* tracks options with non-auto values but tuned by user */ diff --git a/src/page-ops.c b/src/page-ops.c index b25c860e..d07cde07 100644 --- a/src/page-ops.c +++ b/src/page-ops.c @@ -752,12 +752,35 @@ __hot int __must_check_result page_dirty(MDBX_txn *txn, page_t *mp, return MDBX_SUCCESS; } -size_t page_subleaf2_reserve(const MDBX_env *const env, size_t host_page_room, +void recalculate_subpage_thresholds(MDBX_env *env) { + size_t whole = env->leaf_nodemax - NODESIZE; + env->subpage_limit = (whole * env->options.subpage.limit + 32767) >> 16; + whole = env->subpage_limit; + env->subpage_reserve_limit = + (whole * env->options.subpage.reserve_limit + 32767) >> 16; + eASSERT(env, env->leaf_nodemax >= env->subpage_limit + NODESIZE); + eASSERT(env, env->subpage_limit >= env->subpage_reserve_limit); + + whole = env->leaf_nodemax; + env->subpage_room_threshold = + (whole * env->options.subpage.room_threshold + 32767) >> 16; + env->subpage_reserve_prereq = + (whole * env->options.subpage.reserve_prereq + 32767) >> 16; + if (env->subpage_room_threshold + env->subpage_reserve_limit > + (intptr_t)page_space(env)) + env->subpage_reserve_prereq = page_space(env); + else if (env->subpage_reserve_prereq < + env->subpage_room_threshold + env->subpage_reserve_limit) + env->subpage_reserve_prereq = + env->subpage_room_threshold + env->subpage_reserve_limit; + eASSERT(env, env->subpage_reserve_prereq > + env->subpage_room_threshold + env->subpage_reserve_limit); +} + +size_t page_subleaf2_reserve(const MDBX_env *env, size_t host_page_room, size_t subpage_len, size_t item_len) { eASSERT(env, (subpage_len & 1) == 0); - eASSERT(env, env->subpage_reserve_prereq > env->subpage_room_threshold + - env->subpage_reserve_limit && - env->leaf_nodemax >= env->subpage_limit + NODESIZE); + eASSERT(env, env->leaf_nodemax >= env->subpage_limit + NODESIZE); size_t reserve = 0; for (size_t n = 0; n < 5 && reserve + item_len <= env->subpage_reserve_limit && diff --git a/src/page-ops.h b/src/page-ops.h index 5e58ab77..63cdd0b5 100644 --- a/src/page-ops.h +++ b/src/page-ops.h @@ -171,7 +171,7 @@ static inline void page_wash(MDBX_txn *txn, size_t di, page_t *const mp, pgno2bytes(txn->env, npages) - PAGEHDRSZ); } -MDBX_INTERNAL size_t page_subleaf2_reserve(const MDBX_env *const env, +MDBX_INTERNAL size_t page_subleaf2_reserve(const MDBX_env *env, size_t host_page_room, size_t subpage_len, size_t item_len); diff --git a/src/proto.h b/src/proto.h index 5c752405..ebee21a3 100644 --- a/src/proto.h +++ b/src/proto.h @@ -97,6 +97,7 @@ MDBX_INTERNAL int __must_check_result tree_rebalance(MDBX_cursor *mc); MDBX_INTERNAL int __must_check_result tree_propagate_key(MDBX_cursor *mc, const MDBX_val *key); MDBX_INTERNAL void recalculate_merge_thresholds(MDBX_env *env); +MDBX_INTERNAL void recalculate_subpage_thresholds(MDBX_env *env); /* subdb.c */ MDBX_INTERNAL int __must_check_result sdb_fetch(MDBX_txn *txn, size_t dbi);