diff --git a/src/chk.c b/src/chk.c index bf91d109..ec8feaf2 100644 --- a/src/chk.c +++ b/src/chk.c @@ -196,6 +196,49 @@ __cold static MDBX_chk_line_t *chk_print_size(MDBX_chk_line_t *line, const char return line; } +__cold static MDBX_chk_line_t *chk_print_ratio(MDBX_chk_line_t *line, size_t numerator, size_t denominator, + unsigned precision) { + if (line) { + ratio2digits_buffer_t buffer; + line = chk_puts(line, ratio2digits(numerator, denominator, &buffer, precision)); + } + return line; +} + +__cold static MDBX_chk_line_t *chk_print_percent(MDBX_chk_line_t *line, const char *triplet, size_t value, size_t whole, + const char *unit) { + if (line) { + const char *s1 = triplet; + const char *s2 = s1 + strlen(s1) + 1; + const char *s3 = s2 + strlen(s2) + 1; + ratio2digits_buffer_t buffer; + line = chk_print(line, "%s %" PRIuSIZE "%s%s (%s%%%s)%s", s1, value, unit, &"s"[*unit == 0 || value == 1], + ratio2percent(value, whole, &buffer), s2, s3); + } + return line; +} + +__cold static MDBX_chk_line_t *chk_print_pages_percent(MDBX_chk_line_t *line, const char *triplet, size_t pages, + size_t whole) { + return chk_print_percent(line, triplet, pages, whole, " page"); +} + +__cold static MDBX_chk_line_t *chk_print_bytes_percent(MDBX_chk_line_t *line, const char *triplet, size_t pages, + size_t whole) { + return chk_print_percent(line, triplet, pages, whole, " byte"); +} + +__cold static MDBX_chk_line_t *chk_print_pages_percent_bb(MDBX_chk_line_t *line, const char *prefix, size_t pages, + size_t backed, size_t boundary) { + if (line) { + ratio2digits_buffer_t buffer_backed, buffer_boundary; + line = + chk_print(line, "%s %" PRIuSIZE " page%s (%s%% of backed, %s%% of boundary)", prefix, pages, &"s"[pages == 1], + ratio2percent(pages, backed, &buffer_backed), ratio2percent(pages, boundary, &buffer_boundary)); + } + return line; +} + __cold static int chk_error_rc(MDBX_chk_scope_t *const scope, int err, const char *subj) { MDBX_chk_line_t *line = chk_line_begin(scope, MDBX_chk_error); if (line) @@ -964,13 +1007,11 @@ __cold static int chk_tree(MDBX_chk_scope_t *const scope) { tbl->pages.nested_leaf, tbl->pages.nested_subleaf); } + line = chk_line_feed(line); const size_t bytes = pgno2bytes(env, tbl->pages.all); - line = - chk_print(chk_line_feed(line), - "page filling: subtotal %" PRIuSIZE " bytes (%.1f%%), payload %" PRIuSIZE - " (%.1f%%), unused %" PRIuSIZE " (%.1f%%)", - bytes, bytes * 100.0 / total_page_bytes, tbl->payload_bytes, tbl->payload_bytes * 100.0 / bytes, - bytes - tbl->payload_bytes, (bytes - tbl->payload_bytes) * 100.0 / bytes); + line = chk_print_bytes_percent(line, "page filling: subtotal\0\0", bytes, total_page_bytes); + line = chk_print_percent(line, ", payload\0\0", tbl->payload_bytes, bytes, ""); + line = chk_print_percent(line, ", unused\0\0", bytes - tbl->payload_bytes, bytes, ""); if (tbl->pages.empty) line = chk_print(line, ", %" PRIuSIZE " empty pages", tbl->pages.empty); if (tbl->lost_bytes) @@ -989,14 +1030,11 @@ __cold static int chk_tree(MDBX_chk_scope_t *const scope) { } MDBX_chk_line_t *line = chk_line_begin(scope, MDBX_chk_resolution); - line = chk_print(line, - "summary: total %" PRIuSIZE " bytes, payload %" PRIuSIZE " (%.1f%%), unused %" PRIuSIZE " (%.1f%%)," - " average fill %.1f%%", - total_page_bytes, usr->result.total_payload_bytes, - usr->result.total_payload_bytes * 100.0 / total_page_bytes, - total_page_bytes - usr->result.total_payload_bytes, - (total_page_bytes - usr->result.total_payload_bytes) * 100.0 / total_page_bytes, - usr->result.total_payload_bytes * 100.0 / total_page_bytes); + line = chk_print(line, "summary: total %" PRIuSIZE " bytes", total_page_bytes); + line = + chk_print_percent(line, ", payload\0 average density\0", usr->result.total_payload_bytes, total_page_bytes, ""); + line = chk_print_percent(line, ", unused\0 average sparsity\0", total_page_bytes - usr->result.total_payload_bytes, + total_page_bytes, ""); if (total.pages.empty) line = chk_print(line, ", %" PRIuSIZE " empty pages", total.pages.empty); if (total.lost_bytes) @@ -1292,8 +1330,9 @@ bailout: chk_line_feed(line); line = histogram_dist(line, &tbl->histogram.multival, "number of multi-values density", "single", false); chk_line_feed(line); - line = chk_print(line, "number of keys %" PRIuSIZE ", average values per key %.1f", - tbl->histogram.multival.count, record_count / (double)tbl->histogram.multival.count); + line = + chk_print(line, "number of keys %" PRIuSIZE ", average values per key ", tbl->histogram.multival.count); + line = chk_print_ratio(line, record_count, tbl->histogram.multival.count, 1); } chk_line_end(line); } @@ -1449,8 +1488,8 @@ __cold static int env_chk(MDBX_chk_scope_t *const scope) { line = chk_puts(line, "is unavailable"); chk_line_end(line); - line = chk_print_size(chk_line_begin(scope, MDBX_chk_verbose), "system io-block ", chk->envinfo.mi_sys_ioblk, ", "); - line = chk_print_size(line, "unified page cache block ", chk->envinfo.mi_sys_upcblk, ""); + line = chk_print_size(chk_line_begin(scope, MDBX_chk_verbose), "system unified page cache block ", + chk->envinfo.mi_sys_upcblk, ""); chk_line_end(line); env->dxb_mmap.filesize = chk->envinfo.mi_dxb_fsize; @@ -1532,9 +1571,18 @@ __cold static int env_chk(MDBX_chk_scope_t *const scope) { chk_line_end(chk_print(line, " > until it will be closed or reopened in read-write mode.")); } #endif /* Windows || Debug */ - line = chk_print_size(chk_line_begin(scope, MDBX_chk_verbose), "space allocated for the dxb-file in a filesystem ", - chk->envinfo.mi_dxb_fallocated, " "); - line = chk_print(line, "%.2f%%", 100.0 * chk->envinfo.mi_dxb_fallocated / chk->envinfo.mi_geo.current); + line = chk_print_size(chk_line_begin(scope, MDBX_chk_verbose), "filesystem: io-block ", chk->envinfo.mi_sys_ioblk, + ", space allocated for the dxb-file "); + if (chk->envinfo.mi_dxb_fallocated == chk->envinfo.mi_geo.current) { + line = chk_puts(line, "exactly"); + } else { + line = chk_print_size( + line, (chk->envinfo.mi_dxb_fallocated > chk->envinfo.mi_geo.current) ? "with excess " : "partially ", + chk->envinfo.mi_dxb_fallocated, " "); + ratio2digits_buffer_t buffer; + line = + chk_print(line, "%s%%", ratio2percent(chk->envinfo.mi_dxb_fallocated, chk->envinfo.mi_geo.current, &buffer)); + } chk_line_end(line); chk_verbose_meta(inner, 0); chk_verbose_meta(inner, 1); @@ -1652,69 +1700,50 @@ __cold static int env_chk(MDBX_chk_scope_t *const scope) { //-------------------------------------------------------------------------- err = chk_scope_begin(chk, 1, MDBX_chk_space, nullptr, nullptr, "Page allocation:"); - const double percent_boundary_reciprocal = 100.0 / txn->geo.upper; - const double percent_backed_reciprocal = 100.0 / usr->result.backed_pages; + const size_t backed = usr->result.backed_pages; + const size_t boundary = txn->geo.upper; const size_t detained = usr->result.gc_pages - usr->result.reclaimable_pages; - const size_t available2boundary = txn->geo.upper - usr->result.alloc_pages + usr->result.reclaimable_pages; - const size_t available2backed = usr->result.backed_pages - usr->result.alloc_pages + usr->result.reclaimable_pages; - const size_t remained2boundary = txn->geo.upper - usr->result.alloc_pages; - const size_t remained2backed = usr->result.backed_pages - usr->result.alloc_pages; + const size_t available2boundary = boundary - usr->result.alloc_pages + usr->result.reclaimable_pages; + const size_t available2backed = backed - usr->result.alloc_pages + usr->result.reclaimable_pages; + const size_t remained2boundary = boundary - usr->result.alloc_pages; + const size_t remained2backed = backed - usr->result.alloc_pages; const size_t used = (chk->flags & MDBX_CHK_SKIP_BTREE_TRAVERSAL) ? usr->result.alloc_pages - usr->result.gc_pages : usr->result.processed_pages; line = chk_line_begin(usr->scope, MDBX_chk_info); - line = chk_print(line, - "backed by file: %" PRIuSIZE " pages (%.1f%%)" - ", %" PRIuSIZE " left to boundary (%.1f%%)", - usr->result.backed_pages, usr->result.backed_pages * percent_boundary_reciprocal, - txn->geo.upper - usr->result.backed_pages, - (txn->geo.upper - usr->result.backed_pages) * percent_boundary_reciprocal); + line = chk_print_pages_percent(line, "backed by file:\0 of boundary\0", backed, boundary); + line = chk_print_pages_percent(line, ",\0\0 left to boundary", boundary - backed, boundary); line = chk_line_feed(line); - line = chk_print(line, "%s: %" PRIuSIZE " page(s), %.1f%% of backed, %.1f%% of boundary", "used", used, - used * percent_backed_reciprocal, used * percent_boundary_reciprocal); + line = chk_print_pages_percent_bb(line, "used:", used, backed, boundary); line = chk_line_feed(line); - line = chk_print(line, "%s: %" PRIuSIZE " page(s) (%.1f%%) of backed, %" PRIuSIZE " to boundary (%.1f%% of boundary)", - "remained", remained2backed, remained2backed * percent_backed_reciprocal, remained2boundary, - remained2boundary * percent_boundary_reciprocal); + line = chk_print_pages_percent(line, "remained:\0\0 of backed", remained2backed, backed); + line = chk_print_pages_percent(line, ", left\0\0 to boundary", remained2boundary, boundary); line = chk_line_feed(line); - line = - chk_print(line, - "reclaimable: %" PRIuSIZE " (%.1f%% of backed, %.1f%% of boundary)" - ", GC %" PRIuSIZE " (%.1f%% of backed, %.1f%% of boundary)", - usr->result.reclaimable_pages, usr->result.reclaimable_pages * percent_backed_reciprocal, - usr->result.reclaimable_pages * percent_boundary_reciprocal, usr->result.gc_pages, - usr->result.gc_pages * percent_backed_reciprocal, usr->result.gc_pages * percent_boundary_reciprocal); + line = chk_print_pages_percent_bb(line, "reclaimable:", usr->result.reclaimable_pages, backed, boundary); + line = chk_print_pages_percent_bb(line, ", within GC", usr->result.gc_pages, backed, boundary); line = chk_line_feed(line); - line = chk_print(line, - "detained by reader(s): %" PRIuSIZE " (%.1f%% of backed, %.1f%% of boundary)" - ", %u reader(s), lag %" PRIi64, - detained, detained * percent_backed_reciprocal, detained * percent_boundary_reciprocal, - chk->envinfo.mi_numreaders, chk->envinfo.mi_recent_txnid - chk->envinfo.mi_latter_reader_txnid); + line = chk_print_pages_percent_bb(line, "detained by reader(s):", detained, backed, boundary); + line = chk_print(line, ", %u reader(s), lag %" PRIi64, chk->envinfo.mi_numreaders, + chk->envinfo.mi_recent_txnid - chk->envinfo.mi_latter_reader_txnid); line = chk_line_feed(line); - line = chk_print(line, "%s: %" PRIuSIZE " page(s), %.1f%% of backed, %.1f%% of boundary", "allocated", - usr->result.alloc_pages, usr->result.alloc_pages * percent_backed_reciprocal, - usr->result.alloc_pages * percent_boundary_reciprocal); + line = chk_print_pages_percent_bb(line, "allocated:", usr->result.alloc_pages, backed, boundary); line = chk_line_feed(line); - line = chk_print(line, "%s: %" PRIuSIZE " page(s) (%.1f%%) of backed, %" PRIuSIZE " to boundary (%.1f%% of boundary)", - "available", available2backed, available2backed * percent_backed_reciprocal, available2boundary, - available2boundary * percent_boundary_reciprocal); + line = chk_print_pages_percent(line, "available:\0 of backed\0", available2backed, backed); + line = chk_print_pages_percent(line, ", left\0\0 to boundary", available2boundary, boundary); chk_line_end(line); line = chk_line_begin(usr->scope, MDBX_chk_resolution); - line = chk_print(line, "%s %" PRIaPGNO " pages", (txn->geo.upper == txn->geo.now) ? "total" : "upto", txn->geo.upper); - line = chk_print(line, ", backed %" PRIuSIZE " (%.1f%%)", usr->result.backed_pages, - usr->result.backed_pages * percent_boundary_reciprocal); - line = chk_print(line, ", allocated %" PRIuSIZE " (%.1f%%)", usr->result.alloc_pages, - usr->result.alloc_pages * percent_boundary_reciprocal); - line = chk_print(line, ", available %" PRIuSIZE " (%.1f%%)", available2boundary, - available2boundary * percent_boundary_reciprocal); + line = chk_print(line, "%s %zu pages", (boundary == txn->geo.now) ? "total" : "upto", boundary); + line = chk_print_pages_percent(line, ", backed\0\0", backed, boundary); + line = chk_print_pages_percent(line, ", allocated\0\0", usr->result.alloc_pages, boundary); + line = chk_print_pages_percent(line, ", available\0\0", available2boundary, boundary); chk_line_end(line); chk_scope_restore(scope, err); diff --git a/src/gc-put.c b/src/gc-put.c index 80a82059..05b3f93c 100644 --- a/src/gc-put.c +++ b/src/gc-put.c @@ -1290,9 +1290,12 @@ static int gc_fill_returned(MDBX_txn *txn, gcu_t *ctx) { rkl_iter_t iter = rkl_iterator(&txn->wr.gc.comeback, is_lifo(txn)); size_t surplus = ctx->return_reserved_hi - amount, stored = 0; const uint64_t factor = ((uint64_t)surplus << 32) / ctx->return_reserved_hi; - TRACE("%s: amount %zu, slots %zu, surplus %zu (%zu..%zu), factor %.6f (%" PRIu64 " >> 32, sharp %.12f)", - dbg_prefix(ctx), amount, slots, surplus, ctx->return_reserved_lo, ctx->return_reserved_hi, - factor / (double)UINT32_MAX, factor, surplus / (double)ctx->return_reserved_hi); + ratio2digits_buffer_t factor_rough, factor_sharp; + TRACE("%s: amount %zu, slots %zu, surplus %zu (%zu..%zu), factor %s (%" PRIu64 " >> 32, sharp %s)", dbg_prefix(ctx), + amount, slots, surplus, ctx->return_reserved_lo, ctx->return_reserved_hi, + ratio2digits(factor, UINT32_MAX, &factor_rough, 6), factor, + ratio2digits(surplus, ctx->return_reserved_hi, &factor_sharp, 12)); + do { const size_t left = amount - stored; tASSERT(txn, left > 0 && left <= amount); diff --git a/src/utils.c b/src/utils.c index 07a8deff..11869a7a 100644 --- a/src/utils.c +++ b/src/utils.c @@ -41,3 +41,52 @@ MDBX_NOTHROW_CONST_FUNCTION uint64_t rrxmrrxmsx_0(uint64_t v) { v *= UINT64_C(0x9FB21C651E98DF25); return v ^ v >> 28; } + +__cold char *ratio2digits(const uint64_t v, const uint64_t d, ratio2digits_buffer_t *const buffer, int precision) { + assert(d > 0 && precision < 20); + char *const dot = buffer->string + 21; + uint64_t i = v / d, f = v % d, m = d; + + char *tail = dot; + bool carry = m - f < m / 2; + if (precision > 0) { + *tail = '.'; + do { + while (unlikely(f > UINT64_MAX / 10)) { + f >>= 1; + m >>= 1; + } + f *= 10; + assert(tail > buffer->string && tail < ARRAY_END(buffer->string) - 1); + *++tail = '0' + (char)(f / m); + f %= m; + } while (--precision && tail < ARRAY_END(buffer->string) - 1); + + carry = m - f < m / 2; + for (char *scan = tail; carry && scan > dot; --scan) + *scan = (carry = *scan == '9') ? '0' : *scan + 1; + } + assert(tail > buffer->string && tail < ARRAY_END(buffer->string) - 1); + *++tail = '\0'; + + char *head = dot; + i += carry; + while (i > 9) { + assert(head > buffer->string && head < ARRAY_END(buffer->string)); + *--head = '0' + (char)(i % 10); + i /= 10; + } + assert(head > buffer->string && head < ARRAY_END(buffer->string)); + *--head = '0' + (char)i; + + return head; +} + +__cold char *ratio2percent(uint64_t value, uint64_t whole, ratio2digits_buffer_t *buffer) { + while (unlikely(value > UINT64_MAX / 100)) { + value >>= 1; + whole >>= 1; + } + const bool rough = whole >= value && (!value || value > whole / 16); + return ratio2digits(value * 100, whole, buffer, rough ? 1 : 2); +} diff --git a/src/utils.h b/src/utils.h index a77b3a30..0371ca37 100644 --- a/src/utils.h +++ b/src/utils.h @@ -76,3 +76,10 @@ MDBX_MAYBE_UNUSED static inline uint64_t monotime_since_cached(uint64_t begin_ti } return cache->value - begin_timestamp; } + +typedef struct ratio2digits_buffer { + char string[1 + 20 + 1 + 19 + 1]; +} ratio2digits_buffer_t; + +char *ratio2digits(const uint64_t v, const uint64_t d, ratio2digits_buffer_t *const buffer, int precision); +char *ratio2percent(const uint64_t v, const uint64_t d, ratio2digits_buffer_t *const buffer);