From e45e467b1a385df7b45b46cc137a763b6a3628e0 Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Thu, 10 Sep 2026 00:08:58 +0200 Subject: [PATCH] Process content info values in contiguous runs --- cmdline/elem.h | 74 ++++++++++++++++++++++++++++++++++++++++++++++ cmdline/selftest.c | 45 ++++++++++++++++++++++++++++ cmdline/state.c | 25 ++++------------ 3 files changed, 125 insertions(+), 19 deletions(-) diff --git a/cmdline/elem.h b/cmdline/elem.h index a49fc18b..126a7f09 100644 --- a/cmdline/elem.h +++ b/cmdline/elem.h @@ -1395,6 +1395,80 @@ static inline snapraid_info info_get(tommy_arrayblkof* array, block_off_t pos) return info; } +/** + * Set a run of info values. + * + * Grow the array once and process each internal block directly, avoiding the + * per-position grow and lookup performed by info_set(). + */ +static inline void info_set_run(tommy_arrayblkof* array, block_off_t pos, block_off_t count, snapraid_info info) +{ + tommy_arrayblkof_grow(array, pos + count); + + while (count) { + block_off_t offset = pos % TOMMY_ARRAYBLKOF_SIZE; + block_off_t run = TOMMY_ARRAYBLKOF_SIZE - offset; + snapraid_info* data; + block_off_t i; + + if (run > count) + run = count; + + data = tommy_arrayblkof_ref(array, pos); + for (i = 0; i < run; ++i) + data[i] = info; + + pos += run; + count -= run; + } +} + +/** + * Get the value and length of an info run, up to count positions. + * + * Unallocated positions have the same zero value returned by info_get(). + * Process allocated storage a block at a time to avoid resolving every array + * position independently. + */ +static inline block_off_t info_get_run(tommy_arrayblkof* array, block_off_t pos, block_off_t count, snapraid_info* info) +{ + block_off_t size = tommy_arrayblkof_size(array); + block_off_t total = 0; + + if (pos >= size) { + *info = 0; + return count; + } + + memcpy(info, tommy_arrayblkof_ref(array, pos), sizeof(snapraid_info)); + + while (total < count && pos + total < size) { + block_off_t current = pos + total; + block_off_t offset = current % TOMMY_ARRAYBLKOF_SIZE; + block_off_t run = TOMMY_ARRAYBLKOF_SIZE - offset; + snapraid_info* data; + block_off_t i; + + if (run > count - total) + run = count - total; + if (run > size - current) + run = size - current; + + data = tommy_arrayblkof_ref(array, current); + for (i = 0; i < run; ++i) + if (data[i] != *info) + return total + i; + + total += run; + } + + /* info_get() returns zero for the whole unallocated tail */ + if (total < count && *info == 0) + return count; + + return total; +} + /****************************************************************************/ /* bucket */ diff --git a/cmdline/selftest.c b/cmdline/selftest.c index 6eb426d9..b0cd9daa 100644 --- a/cmdline/selftest.c +++ b/cmdline/selftest.c @@ -600,6 +600,50 @@ bail: /* LCOV_EXCL_STOP */ } +static void test_info_run(void) +{ + tommy_arrayblkof array; + const block_off_t start = TOMMY_ARRAYBLKOF_SIZE - 2; + const block_off_t count = TOMMY_ARRAYBLKOF_SIZE + 4; + const snapraid_info first = 0x100; + const snapraid_info second = 0x200; + snapraid_info info; + + tommy_arrayblkof_init(&array, sizeof(snapraid_info)); + + /* check an entirely unallocated zero run */ + if (info_get_run(&array, 0, count, &info) != count || info != 0) + goto bail; + + /* cross two internal array block boundaries */ + info_set_run(&array, start, count, first); + if (info_get_run(&array, 0, start + count, &info) != start || info != 0) + goto bail; + if (info_get_run(&array, start, count + 1, &info) != count || info != first) + goto bail; + if (info_get_run(&array, start + count, count, &info) != count || info != 0) + goto bail; + + /* split the first run without changing the allocated size */ + info_set_run(&array, start + 100, 10, second); + if (info_get_run(&array, start, count, &info) != 100 || info != first) + goto bail; + if (info_get_run(&array, start + 100, count, &info) != 10 || info != second) + goto bail; + if (info_get_run(&array, start + 110, count, &info) != count - 110 || info != first) + goto bail; + + tommy_arrayblkof_done(&array); + return; + +bail: + /* LCOV_EXCL_START */ + tommy_arrayblkof_done(&array); + log_fatal(EINTERNAL, "Failed info run test\n"); + exit(EXIT_FAILURE); + /* LCOV_EXCL_STOP */ +} + struct { const char* pattern; const char* text; @@ -2492,6 +2536,7 @@ void selftest(void) /* tommyds data structures (hash tables, lists, search, sort) */ test_tommy(); + test_info_run(); /* cauchy raid module self-test */ raid_mode(RAID_MODE_CAUCHY_RAID); diff --git a/cmdline/state.c b/cmdline/state.c index 5d4a771a..6b6cfeed 100644 --- a/cmdline/state.c +++ b/cmdline/state.c @@ -2772,14 +2772,9 @@ static void state_read_content(struct snapraid_state* state, const char* path, S bucket_insert(&bucket_hash, t64 + v_oldest, v_count, justsynced); - while (v_count) { - /* insert the info in the array */ - info_set(&state->infoarr, v_pos, info); - - /* go to next block */ - ++v_pos; - --v_count; - } + /* insert the complete run in the info array */ + info_set_run(&state->infoarr, v_pos, v_count, info); + v_pos += v_count; } else { while (v_count) { /* ensure that an info is present only for used positions */ @@ -4405,17 +4400,9 @@ static void* state_write_thread(void* arg) time_t t; unsigned flag; - info = info_get(&state->infoarr, begin); - - /* find the end of run of blocks */ - end = begin + 1; - while (end < blockmax - && info == info_get(&state->infoarr, end) - ) { - ++end; - } - - count = end - begin; + /* find the complete run without resolving each array position */ + count = info_get_run(&state->infoarr, begin, blockmax - begin, &info); + end = begin + count; sputb64(count, f); /* if there is info */