Added an mtree traversal benchmark

Note that because we amortize the traversal cost over the number of
entries, mtree traversal may have some strange looking results when
compared to mtree lookup.

Though it's interesting to note this is a valid result. In mtree lookups
we need to fetch the mdir for each entry, which is expensive. However
mtree traversal can strictly avoid fetching each mdir more than once.
This does make mdir traversal faster when iterating over all mdirs in
order.

This can be represented in big O notation if we treat the number of
entries (n) and block size (b) as variables:

- mtree traversal via lookup    = O(nb+nlog(b)logb(n))
- mtree traversal via traversal = O(nlog(b)logb(n))
This commit is contained in:
Christopher Haster
2023-05-27 14:14:30 -05:00
parent 09b3d24036
commit 6a96866737
+66
View File
@@ -133,3 +133,69 @@ code = '''
lfsr_unmount(&lfs) => 0;
'''
[cases.bench_mtree_traversal]
defines.N = [8, 16, 32, 64, 128, 256, 1024]
# 0 = in-order
# 1 = reversed-order
# 2 = random-order
defines.ORDER = [0, 1, 2]
defines.SEED = 42
defines.VALIDATE = [false, true]
in = 'lfs.c'
code = '''
uint32_t prng = SEED;
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
lfs_t lfs;
lfsr_format(&lfs, cfg) => 0;
lfsr_mount(&lfs, cfg) => 0;
// create an mtree with N entries
for (lfs_size_t i = 0; i < N; i++) {
// choose an mid
lfs_ssize_t mid
= lfsr_mtree_weight(&lfs) == 0 ? -1
: (ORDER == 0) ? (lfs_ssize_t)(lfsr_mtree_weight(&lfs)-1)
: (ORDER == 1) ? 0
: (lfs_ssize_t)(BENCH_PRNG(&prng) % lfsr_mtree_weight(&lfs));
// fetch mdir
lfsr_mdir_t mdir;
lfsr_mtree_lookup(&lfs, mid, &mdir) => 0;
// choose rid
lfs_ssize_t rid
= (ORDER == 0) ? lfsr_mdir_weight(&mdir)
: (ORDER == 1) ? 0
: BENCH_PRNG(&prng) % (lfsr_mdir_weight(&mdir)+1);
// create an entry
lfsr_mdir_commit(&lfs, &mdir, &rid, LFSR_ATTRS(
LFSR_ATTR(rid, MKINLINED, +1, &alphas[i % 26], 1))) => 0;
}
// traverse the mtree
BENCH_START();
lfsr_mtree_traversal_t traversal = LFSR_MTREE_TRAVERSAL_INIT(
VALIDATE ? LFSR_MTREE_TRAVERSAL_VALIDATE : 0);
for (lfs_block_t i = 0;; i++) {
// a bit hacky, but this catches infinite loops
assert(i < 2*(1+N));
lfs_size_t mid_;
lfsr_tag_t tag_;
lfsr_data_t data_;
int err = lfsr_mtree_traversal_next(&lfs, &traversal,
&mid_, &tag_, &data_);
assert(!err || err == LFS_ERR_NOENT);
if (err == LFS_ERR_NOENT) {
break;
}
assert(tag_ == LFSR_TAG_BTREE || tag_ == LFSR_TAG_MDIR);
}
BENCH_STOP();
lfsr_unmount(&lfs) => 0;
'''