diff --git a/Makefile b/Makefile index 25f0ff49..f4dad5f6 100644 --- a/Makefile +++ b/Makefile @@ -600,7 +600,9 @@ bench-marks: SUMMARYFLAGS+=-Si bench-marks: $(BENCH_CSV) $(strip ./scripts/csv.py \ <(./scripts/csv.py $^ \ - -Dprobe=create,delete,fetch,lookup,write \ + -Dprobe=append,remove,create,delete,fetch,lookup,$\ + commit,namelookup,$\ + write,stat,read \ -bprobe='%(case)s+%(probe)s' \ -Fi='min(enumerate())' \ -fn='max(n)' \ @@ -621,14 +623,18 @@ bench-marks-csv: $(BUILDDIR)/lfs3.bench.csv bench-marks-diff: $(BENCH_CSV) $(strip ./scripts/csv.py \ <(./scripts/csv.py $^ \ - -Dprobe=create,delete,fetch,lookup,write \ + -Dprobe=append,remove,create,delete,fetch,lookup,$\ + commit,namelookup,$\ + write,stat,read \ -bprobe='%(case)s+%(probe)s' \ -Fi='min(enumerate())' \ -fn='max(n)' \ -ft='max(float(bench_simtime)/1.0e9)' \ -o-) \ -d <(./scripts/csv.py $(BUILDDIR)/lfs3.bench.csv \ - -Dprobe=create,delete,fetch,lookup,write \ + -Dprobe=append,remove,create,delete,fetch,lookup,$\ + commit,namelookup,$\ + write,stat,read \ -bprobe='%(case)s+%(probe)s' \ -Fi='min(enumerate())' \ -fn='max(n)' \ @@ -644,7 +650,9 @@ bench-bottlenecks: SUMMARYFLAGS+=-Sruntime bench-bottlenecks: $(BENCH_CSV) $(strip ./scripts/csv.py \ <(./scripts/csv.py $^ \ - -Dprobe=create,delete,fetch,lookup,write \ + -Dprobe=append,remove,create,delete,fetch,lookup,$\ + commit,namelookup,$\ + write,stat,read \ -bprobe='%(case)s+%(probe)s' \ -Fi='min(enumerate())' \ -fn='max(n)' \ @@ -662,7 +670,9 @@ bench-bottlenecks: $(BENCH_CSV) bench-ops: SUMMARYFLAGS+=-Si bench-ops: $(BENCH_CSV) $(strip ./scripts/csv.py $^ \ - -Dprobe=create,delete,fetch,lookup,write \ + -Dprobe=append,remove,create,delete,fetch,lookup,$\ + commit,namelookup,$\ + write,stat,read \ -bprobe='%(case)s+%(probe)s' \ -Fi='min(enumerate())' \ -freads='bench_reads' \ @@ -678,7 +688,9 @@ bench-ops: $(BENCH_CSV) bench-widths: SUMMARYFLAGS+=-Si bench-widths: $(BENCH_CSV) $(strip ./scripts/csv.py $^ \ - -Dprobe=create,delete,fetch,lookup,write \ + -Dprobe=append,remove,create,delete,fetch,lookup,$\ + commit,namelookup,$\ + write,stat,read \ -bprobe='%(case)s+%(probe)s' \ -Fi='min(enumerate())' \ -freaded="avg(ffrac( \ diff --git a/benches/bench_btree.toml b/benches/bench_btree.toml new file mode 100644 index 00000000..3bde191f --- /dev/null +++ b/benches/bench_btree.toml @@ -0,0 +1,283 @@ +# Low-level B-tree benchmarks +after = ['bench_rbyd'] + +# limit to littlefs3 without a gbmap +ifndef = 'LFS3_GBMAP' + +# maximize lookahead buffer, we don't actually gc so we only get one pass +# of the disk for these tests +defines.LOOKAHEAD_SIZE = '(BLOCK_COUNT+8-1) / 8' + + +[cases.bench_btree_ids] +# 0 = in-order +# 1 = reversed-order +# 2 = random-order +defines.ORDER = 2 +defines.N = 8192 +defines.SIZE = 4 +defines.STEP = 1 +defines.SEED = 42 +# set of probes to measure +# 0x1 => commit +# 0x2 => lookup +# 0x8 => usage +defines.MASK = 0xb +in = 'lfs3.c' +code = ''' + lfs3_t lfs3; + lfs3_init(&lfs3, LFS3_M_RDWR, CFG) => 0; + // create free lookahead + memset(lfs3.lookahead.buffer, 0, CFG->lookahead_size); + lfs3.lookahead.window = 2; + lfs3.lookahead.off = 0; + lfs3.lookahead.known = lfs3_min(8*CFG->lookahead_size, + CFG->block_count-2); + lfs3_alloc_ckpoint(&lfs3); + + // commit initial commit just to avoid first erase messing with + // amortized results + lfs3_btree_t btree; + lfs3_btree_init(&btree); + lfs3_btree_commit(&lfs3, &btree, 0, LFS3_RATTRS( + LFS3_RATTR(1, LFS3_TAG_DATA, +1), + LFS3_RATTR(1, LFS3_TAG_DATA, -1), + LFS3_RATTR_NULL)) => 0; + + // fill btree with N elements + uint32_t prng = SEED; + for (lfs3_bid_t i = 0; i < N; i++) { + lfs3_bid_t i_ + = (ORDER == 0) ? i + : (ORDER == 1) ? 0 + : BENCH_PRNG(&prng) % (btree.r.weight+1); + // measure commits + if ((MASK & 0x1) && (i+1) % STEP == 0) { + BENCH_START("commit"); + } + uint8_t wbuf[SIZE]; + memset(wbuf, 'a'+(BENCH_PRNG(&prng) % 26), SIZE); + lfs3_btree_commit(&lfs3, &btree, i_, LFS3_RATTRS( + LFS3_RATTR(3, LFS3_TAG_DATA, +1, LFS3_FROM_DATA), + LFS3_RATTR_ARG(SIZE), + LFS3_RATTR_ARG(wbuf), + LFS3_RATTR_NULL)) => 0; + if ((MASK & 0x1) && (i+1) % STEP == 0) { + BENCH_STOP("commit", i+1); + } + + // measure lookups + if ((MASK & 0x2) && (i+1) % STEP == 0) { + BENCH_START("lookup"); + lfs3_bid_t i_ = BENCH_PRNG(&prng) % btree.r.weight; + lfs3_data_t data_; + lfs3_stag_t tag_ = lfs3_btree_lookup(&lfs3, &btree, + i_, LFS3_TAG_DATA, + &data_); + assert(tag_ == LFS3_TAG_DATA); + assert(lfs3_data_size(&data_) == SIZE); + BENCH_STOP("lookup", i+1); + } + + // measure disk usage + if ((MASK & 0x8) && (i+1) % STEP == 0) { + lfs3_off_t count = 0; + + lfs3_btrv_t btrv; + lfs3_btrv_init(&btrv); + while (true) { + lfs3_sbid_t bid_; + lfs3_bid_t weight_; + lfs3_data_t data_; + lfs3_stag_t tag_ = lfs3_btree_traverse(&lfs3, &btree, + &btrv, + &bid_, &weight_, &data_); + assert(tag_ >= 0 || tag_ == LFS3_ERR_NOENT); + if (tag_ == LFS3_ERR_NOENT) { + break; + } + + if (tag_ == LFS3_TAG_BRANCH) { + count += 1; + } + } + + BENCH_RESULT("usage", i+1, + (uintmax_t)count * CFG->block_size); + } + } +''' + + +[cases.bench_btree_names] +# 0 = in-order +# 1 = reversed-order +# 2 = random-order +defines.ORDER = 2 +defines.N = 8192 +defines.SIZE = 4 +defines.STEP = 1 +defines.SEED = 42 +# set of probes to measure +# 0x1 => commit +# 0x2 => lookup +# 0x4 => namelookup +# 0x8 => usage +defines.MASK = 0xf +in = 'lfs3.c' +code = ''' + lfs3_t lfs3; + lfs3_init(&lfs3, LFS3_M_RDWR, CFG) => 0; + // create free lookahead + memset(lfs3.lookahead.buffer, 0, CFG->lookahead_size); + lfs3.lookahead.window = 2; + lfs3.lookahead.off = 0; + lfs3.lookahead.known = lfs3_min(8*CFG->lookahead_size, + CFG->block_count-2); + lfs3_alloc_ckpoint(&lfs3); + + // commit initial commit just to avoid first erase messing with + // amortized results + lfs3_btree_t btree; + lfs3_btree_init(&btree); + lfs3_btree_commit(&lfs3, &btree, 0, LFS3_RATTRS( + LFS3_RATTR(1, LFS3_TAG_DATA, +1), + LFS3_RATTR(1, LFS3_TAG_DATA, -1), + LFS3_RATTR_NULL)) => 0; + + // fill btree with N elements + uint32_t prng = SEED; + for (lfs3_bid_t i = 0; i < N; i++) { + lfs3_bid_t i_ + = (ORDER == 0) ? i + : (ORDER == 1) ? N-1-i + : BENCH_PRNG(&prng) % N; + // measure commits + if ((MASK & 0x1) && (i+1) % STEP == 0) { + BENCH_START("commit"); + } + char name[256]; + sprintf(name, "e%05d", i_); + uint8_t wbuf[SIZE]; + memset(wbuf, 'a'+(BENCH_PRNG(&prng) % 26), SIZE); + // we need to map names to bids before we can commit, so include + // this in our commit cost + lfs3_bid_t bid; + lfs3_tag_t tag; + lfs3_bid_t weight; + lfs3_data_t data; + lfs3_scmp_t cmp = lfs3_btree_namelookup(&lfs3, &btree, + 0, name, strlen(name), + &bid, &tag, &weight, &data); + assert(cmp >= 0 || cmp == LFS3_ERR_NOENT); + // empty btree? + if (cmp == LFS3_ERR_NOENT) { + lfs3_btree_commit(&lfs3, &btree, 0, LFS3_RATTRS( + LFS3_RATTR(3, LFS3_TAG_REG, +1, LFS3_FROM_NAME), + LFS3_RATTR_ARG(0), + LFS3_RATTR_ARG(name), + LFS3_RATTR(3, LFS3_TAG_DATA, 0, LFS3_FROM_DATA), + LFS3_RATTR_ARG(SIZE), + LFS3_RATTR_ARG(wbuf), + LFS3_RATTR_NULL)) => 0; + // insert before? + } else if (cmp > LFS3_CMP_EQ) { + lfs3_btree_commit(&lfs3, &btree, bid, LFS3_RATTRS( + LFS3_RATTR(3, LFS3_TAG_REG, +1, LFS3_FROM_NAME), + LFS3_RATTR_ARG(0), + LFS3_RATTR_ARG(name), + LFS3_RATTR(3, LFS3_TAG_DATA, 0, LFS3_FROM_DATA), + LFS3_RATTR_ARG(SIZE), + LFS3_RATTR_ARG(wbuf), + LFS3_RATTR_NULL)) => 0; + // insert after? + } else if (cmp < LFS3_CMP_EQ) { + lfs3_btree_commit(&lfs3, &btree, bid, LFS3_RATTRS( + // yes, we need this noop, triggers an insert after + LFS3_RATTR(1, LFS3_RATTR_NULL, 0), + LFS3_RATTR(3, LFS3_TAG_REG, +1, LFS3_FROM_NAME), + LFS3_RATTR_ARG(0), + LFS3_RATTR_ARG(name), + LFS3_RATTR(3, LFS3_TAG_DATA, 0, LFS3_FROM_DATA), + LFS3_RATTR_ARG(SIZE), + LFS3_RATTR_ARG(wbuf), + LFS3_RATTR_NULL)) => 0; + // replace? + } else { + lfs3_btree_commit(&lfs3, &btree, bid, LFS3_RATTRS( + LFS3_RATTR(3, LFS3_TAG_DATA, 0, LFS3_FROM_DATA), + LFS3_RATTR_ARG(SIZE), + LFS3_RATTR_ARG(wbuf), + LFS3_RATTR_NULL)) => 0; + } + if ((MASK & 0x1) && (i+1) % STEP == 0) { + BENCH_STOP("commit", i+1); + } + + // measure namelookups + if ((MASK & 0x4) && (i+1) % STEP == 0) { + BENCH_START("namelookup"); + lfs3_bid_t i_ = BENCH_PRNG(&prng) % N; + char name[256]; + sprintf(name, "e%05d", i_); + lfs3_bid_t bid_; + lfs3_tag_t tag_; + lfs3_bid_t weight_; + lfs3_data_t data_; + lfs3_scmp_t cmp_ = lfs3_btree_namelookup(&lfs3, &btree, + 0, name, strlen(name), + &bid_, &tag_, &weight_, &data_); + assert(cmp_ >= 0); + if (cmp_ == LFS3_CMP_EQ) { + assert(tag_ == LFS3_TAG_REG); + assert(weight_ == 1); + assert(lfs3_data_size(&data_) == 1+strlen(name)); + } + BENCH_STOP("namelookup", i+1); + } + + // measure lookups + if ((MASK & 0x2) && (i+1) % STEP == 0) { + BENCH_START("lookup"); + lfs3_bid_t i_ = BENCH_PRNG(&prng) % btree.r.weight; + lfs3_data_t data_; + lfs3_stag_t tag_ = lfs3_btree_lookup(&lfs3, &btree, + i_, LFS3_TAG_DATA, + &data_); + assert(tag_ == LFS3_TAG_DATA + || tag_ == LFS3_ERR_NOENT); + if (tag_ != LFS3_ERR_NOENT) { + assert(tag_ == LFS3_TAG_DATA); + assert(lfs3_data_size(&data_) == SIZE); + } + BENCH_STOP("lookup", i+1); + } + + // measure disk usage + if ((MASK & 0x8) && (i+1) % STEP == 0) { + lfs3_off_t count = 0; + + lfs3_btrv_t btrv; + lfs3_btrv_init(&btrv); + while (true) { + lfs3_sbid_t bid_; + lfs3_bid_t weight_; + lfs3_data_t data_; + lfs3_stag_t tag_ = lfs3_btree_traverse(&lfs3, &btree, + &btrv, + &bid_, &weight_, &data_); + assert(tag_ >= 0 || tag_ == LFS3_ERR_NOENT); + if (tag_ == LFS3_ERR_NOENT) { + break; + } + + if (tag_ == LFS3_TAG_BRANCH) { + count += 1; + } + } + + BENCH_RESULT("usage", i+1, + (uintmax_t)count * CFG->block_size); + } + } +''' diff --git a/benches/bench_dir.toml b/benches/bench_dir.toml new file mode 100644 index 00000000..b614f382 --- /dev/null +++ b/benches/bench_dir.toml @@ -0,0 +1,93 @@ +# Simple (root) directory benchmark +after = ['bench_rbyd', 'bench_btree', 'bench_file'] + +# helper for measuring disk usage +code = ''' + #include "benches/bench_helpers.h" +''' + + +[cases.bench_dir] +# 0 = in-order +# 1 = reversed-order +# 2 = random-order +defines.ORDER = 2 +defines.N = 1024 +defines.SIZE = 64 +defines.STEP = 1 +defines.SEED = 42 +# set of probes to measure +# 0x1 => write +# 0x2 => stat +# 0x4 => read +# 0x8 => usage +defines.MASK = 0xf +code = ''' + lfs3_t lfs3; + lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; + uint32_t prng = SEED; + + for (lfs3_off_t i = 0; i < N; i++) { + lfs3_off_t i_ + = (ORDER == 0) ? i + : (ORDER == 1) ? N-1-i + : BENCH_PRNG(&prng) % N; + // measure file creation + if ((MASK & 0x1) && (i+1) % STEP == 0) { + BENCH_START("write"); + } + char name[256]; + sprintf(name, "bench_%08x", i_); + lfs3_file_t file; + lfs3_file_open(&lfs3, &file, name, + LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_TRUNC) => 0; + uint8_t wbuf[SIZE]; + for (lfs3_size_t j = 0; j < SIZE; j++) { + wbuf[j] = 'a' + (BENCH_PRNG(&prng) % 26); + } + lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; + lfs3_file_close(&lfs3, &file) => 0; + if ((MASK & 0x1) && (i+1) % STEP == 0) { + BENCH_STOP("write", i*SIZE+SIZE); + } + + // choose a random file to stat + if ((MASK & 0x2) && (i+1) % STEP == 0) { + BENCH_START("stat"); + lfs3_off_t i__ = BENCH_PRNG(&prng) % N; + sprintf(name, "bench_%08x", i__); + struct lfs3_info info; + int err = lfs3_stat(&lfs3, name, &info); + assert(!err || err == LFS3_ERR_NOENT); + if (err != LFS3_ERR_NOENT) { + assert(info.type == LFS3_TYPE_REG); + assert(info.size == SIZE); + } + BENCH_STOP("stat", i*SIZE+SIZE); + } + + // choose a random file to read + if ((MASK & 0x4) && (i+1) % STEP == 0) { + BENCH_START("read"); + lfs3_off_t i__ = BENCH_PRNG(&prng) % N; + sprintf(name, "bench_%08x", i__); + int err = lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY); + assert(!err || err == LFS3_ERR_NOENT); + if (err != LFS3_ERR_NOENT) { + uint8_t rbuf[SIZE]; + lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; + lfs3_file_close(&lfs3, &file) => 0; + } + BENCH_STOP("read", i*SIZE+SIZE); + } + + // measure disk usage + if ((MASK & 0x8) && (i+1) % STEP == 0) { + uintmax_t usage = bench_helpers_usage(&lfs3); + BENCH_RESULT("usage", i*SIZE+SIZE, usage); + } + } + + lfs3_unmount(&lfs3) => 0; +''' diff --git a/benches/bench_file.toml b/benches/bench_file.toml new file mode 100644 index 00000000..0a75719e --- /dev/null +++ b/benches/bench_file.toml @@ -0,0 +1,102 @@ +# Simple file benchmark +after = ['bench_rbyd', 'bench_btree'] + +# helper for measuring disk usage +code = ''' + #include "benches/bench_helpers.h" +''' + + +[cases.bench_file] +# 0 = in-order +# 1 = reversed-order +# 2 = random-order +# 3 = random-unaligned-order +defines.ORDER = 3 +defines.SIZE = '1024*1024' # 1 MiB +defines.CHUNK = 64 +defines.FLUSH = true # note if you don't flush, reads may end up writing +defines.SYNC = false +defines.STEP = 1 +defines.SEED = 42 +# set of probes to measure +# 0x1 => write +# 0x2 => read +# 0x4 => usage +defines.MASK = 0x7 +code = ''' + lfs3_t lfs3; + lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; + uint32_t prng = SEED; + + // open our file + lfs3_file_t file; + lfs3_file_open(&lfs3, &file, "bench_file", + LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; + for (lfs3_size_t i = 0; i < (SIZE+(CHUNK-1))/CHUNK; i++) { + lfs3_off_t i_ + = (ORDER == 0) + ? i*CHUNK + : (ORDER == 1) + ? (((SIZE+(CHUNK-1))/CHUNK)-1-i)*CHUNK + : (ORDER == 2) + ? (BENCH_PRNG(&prng) % ((SIZE+(CHUNK-1))/CHUNK)) * CHUNK + : BENCH_PRNG(&prng) % (lfs3_max(SIZE, CHUNK) - (CHUNK-1)); + // measure a write + if ((MASK & 0x1) && (i+1) % STEP == 0) { + BENCH_START("write"); + } + // seek to where we want to write + lfs3_file_seek(&lfs3, &file, i_, LFS3_SEEK_SET) => i_; + + // write + uint8_t wbuf[CHUNK]; + for (lfs3_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (BENCH_PRNG(&prng) % 26); + } + lfs3_file_write(&lfs3, &file, wbuf, CHUNK) => CHUNK; + + // flush? + if (FLUSH) { + lfs3_file_flush(&lfs3, &file) => 0; + } + // sync? + if (SYNC) { + lfs3_file_sync(&lfs3, &file) => 0; + } + if ((MASK & 0x1) && (i+1) % STEP == 0) { + BENCH_STOP("write", i*CHUNK+CHUNK); + } + + // measure a random chunk-sized read + if ((MASK & 0x2) && (i+1) % STEP == 0) { + BENCH_START("read"); + // align to chunk? + lfs3_off_t i__ = (ORDER <= 2) + ? (BENCH_PRNG(&prng) + % ((lfs3_file_size(&lfs3, &file)+(CHUNK-1))/CHUNK)) + * CHUNK + : BENCH_PRNG(&prng) + % (lfs3_file_size(&lfs3, &file) - (CHUNK-1)); + + // seek to where we want to read + lfs3_file_seek(&lfs3, &file, i__, LFS3_SEEK_SET) => i__; + + // read + uint8_t rbuf[CHUNK]; + lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; + + BENCH_STOP("read", i*CHUNK+CHUNK); + } + + // measure disk usage + if ((MASK & 0x4) && (i+1) % STEP == 0) { + uintmax_t usage = bench_helpers_usage(&lfs3); + BENCH_RESULT("usage", i*CHUNK+CHUNK, usage); + } + } + lfs3_file_close(&lfs3, &file) => 0; + + lfs3_unmount(&lfs3) => 0; +''' diff --git a/benches/bench_rbyd.toml b/benches/bench_rbyd.toml index ab2defb7..1887cfae 100644 --- a/benches/bench_rbyd.toml +++ b/benches/bench_rbyd.toml @@ -10,15 +10,127 @@ defines.BLOCK_COUNT = 1 # don't bother simulating erases, this gets expensive with big disks defines.ERASE_VALUE = -1 -[cases.bench_rbyd] + +[cases.bench_rbyd_attrs] +# 0 = in-order +# 1 = reversed-order +# 2 = random-order +defines.ORDER = 2 +defines.N = 256 +defines.SIZE = 4 +defines.STEP = 1 +defines.SEED = 42 +# set of probes to measure +# 0x01 => append +# 0x02 => remove +# 0x04 => fetch +# 0x08 => lookup +# 0x10 => usage +defines.MASK = 0x1f +in = 'lfs3.c' +code = ''' + lfs3_t lfs3; + lfs3_init(&lfs3, LFS3_M_RDWR, CFG) => 0; + + for (lfs3_size_t n = 1; n < N; n += STEP) { + lfs3_rbyd_t rbyd = { + .blocks[0] = 0, + .eoff = 0, + .cksum = 0, + .trunk = 0, + .weight = 0, + }; + lfs3_bd_erase(&lfs3, rbyd.blocks[0]) => 0; + + // create N attrs + // + // NOTE we only have 256 user attributes, so this benchmark is + // a bit limited + uint32_t prng = SEED; + for (lfs3_size_t i = 0; i < n; i++) { + // create an attr + lfs3_off_t i_ + = (ORDER == 0) ? i + : (ORDER == 1) ? (n-1-i) + : BENCH_PRNG(&prng) % n; + uint8_t wbuf[SIZE]; + memset(wbuf, 'a'+(BENCH_PRNG(&prng) % 26), SIZE); + lfs3_rbyd_commit(&lfs3, &rbyd, -1, LFS3_RATTRS( + LFS3_RATTR(3, + LFS3_TAG_ATTR(i_ & 0xff), 0, LFS3_FROM_DATA), + LFS3_RATTR_ARG(SIZE), + LFS3_RATTR_ARG(wbuf), + LFS3_RATTR_NULL)) => 0; + } + + // create an attr + if (MASK & 0x01) { + BENCH_START("append"); + lfs3_off_t i_ + = (ORDER == 0) ? (n-1) + : (ORDER == 1) ? 0 + : BENCH_PRNG(&prng) % n; + uint8_t wbuf[SIZE]; + memset(wbuf, 'a'+(BENCH_PRNG(&prng) % 26), SIZE); + lfs3_rbyd_commit(&lfs3, &rbyd, -1, LFS3_RATTRS( + LFS3_RATTR(3, + LFS3_TAG_ATTR(i_ & 0xff), 0, LFS3_FROM_DATA), + LFS3_RATTR_ARG(SIZE), + LFS3_RATTR_ARG(wbuf), + LFS3_RATTR_NULL)) => 0; + BENCH_STOP("append", n+STEP); + } + + // delete an attr + if (MASK & 0x02) { + BENCH_START("remove"); + lfs3_off_t i_ = BENCH_PRNG(&prng) % n; + lfs3_rbyd_commit(&lfs3, &rbyd, -1, LFS3_RATTRS( + LFS3_RATTR(1, + LFS3_tag_RM | LFS3_TAG_ATTR(i_ & 0xff), 0), + LFS3_RATTR_NULL)) => 0; + BENCH_STOP("remove", n+STEP); + } + + // fetch the rbyd + if (MASK & 0x04) { + BENCH_START("fetch"); + lfs3_rbyd_t rbyd_; + lfs3_rbyd_fetch(&lfs3, &rbyd_, rbyd.blocks[0], 0) => 0; + BENCH_STOP("fetch", n+STEP); + } + + // lookup an attr + if (MASK & 0x08) { + BENCH_START("lookup"); + lfs3_off_t i_ = BENCH_PRNG(&prng) % n; + lfs3_data_t data_; + lfs3_stag_t tag_ = lfs3_rbyd_lookup(&lfs3, &rbyd, + -1, LFS3_TAG_ATTR(i_ & 0xff), + &data_); + // note that random order may have some collisions + assert(tag_ == LFS3_TAG_ATTR(i_ & 0xff) + || tag_ == LFS3_ERR_NOENT); + BENCH_STOP("lookup", n+STEP); + } + + // measure the disk usage + if (MASK & 0x10) { + BENCH_RESULT("usage", n+STEP, lfs3_rbyd_eoff(&rbyd)); + } + } +''' + + +[cases.bench_rbyd_ids] # 0 = in-order # 1 = reversed-order # 2 = random-order defines.ORDER = 2 defines.N = 1024 +defines.SIZE = 4 defines.STEP = 1 defines.SEED = 42 -defines.SIZE = 4 # set of probes to measure # 0x01 => create # 0x02 => delete @@ -31,7 +143,7 @@ code = ''' lfs3_t lfs3; lfs3_init(&lfs3, LFS3_M_RDWR, CFG) => 0; - for (lfs3_size_t n = 0; n < N; n += STEP) { + for (lfs3_size_t n = 1; n < N; n += STEP) { lfs3_rbyd_t rbyd = { .blocks[0] = 0, .eoff = 0, @@ -83,14 +195,12 @@ code = ''' // delete an attr if (MASK & 0x02) { BENCH_START("delete"); - if (n > 0) { - lfs3_off_t i_ = BENCH_PRNG(&prng) % rbyd.weight; - lfs3_off_t n_ = rbyd.weight; - lfs3_rbyd_commit(&lfs3, &rbyd, i_, LFS3_RATTRS( - LFS3_RATTR(1, LFS3_tag_RM, -1), - LFS3_RATTR_NULL)) => 0; - assert(rbyd.weight == n_-1); - } + lfs3_off_t i_ = BENCH_PRNG(&prng) % rbyd.weight; + lfs3_off_t n_ = rbyd.weight; + lfs3_rbyd_commit(&lfs3, &rbyd, i_, LFS3_RATTRS( + LFS3_RATTR(1, LFS3_tag_RM, -1), + LFS3_RATTR_NULL)) => 0; + assert(rbyd.weight == n_-1); BENCH_STOP("delete", n+STEP); } diff --git a/benches/bench_rt.toml b/benches/bench_rt.toml new file mode 100644 index 00000000..8e167824 --- /dev/null +++ b/benches/bench_rt.toml @@ -0,0 +1,232 @@ +# High-level read-throughput benchmarks +after = ['bench_file', 'bench_dir', 'bench_wt'] + +# these are common and can be overridden suite-wide +# +# note for bench_*_many, file size defaults to CHUNK, and SIZE = sum of +# all files +# +defines.SIZE = '1024*1024' # 1 MiB +defines.CHUNK = 64 +defines.SEED = 42 + +# simulated time, in nanoseconds, to run the bench +defines.SIM_TIME = '60ULL*60ULL*1000ULL*1000ULL*1000ULL' # 1 hour +# simulation size in bytes +defines.SIM_SIZE = 0 + +# set this to true to skip bench warmup +defines.SKIP_WARMUP = false + +# include common bench helpers +code = ''' + #include "benches/bench_helpers.h" +''' + + +# sequential read throughput +[cases.bench_rt_seq] +code = ''' + lfs3_t lfs3; + lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; + if (!SKIP_WARMUP) { + int err = bench_helpers_warmup(&lfs3); + if (err) { + LFS3_ERROR("Bench warmup failed: %d", err); + return; + } + } + uint32_t prng = SEED; + + // reset our timer + lfs3_kiwibd_simreset(CFG); + + // create a file to read + lfs3_file_t file; + lfs3_file_open(&lfs3, &file, "bench_linear", + LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; + for (lfs3_size_t i = 0; i < (SIZE+(CHUNK-1))/CHUNK; i++) { + uint8_t wbuf[CHUNK]; + for (lfs3_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (BENCH_PRNG(&prng) % 26); + } + lfs3_file_write(&lfs3, &file, wbuf, CHUNK) => CHUNK; + + // taking too long? + if (SIM_TIME && lfs3_kiwibd_simtime(CFG) >= SIM_TIME) { + return; + } + } + lfs3_file_close(&lfs3, &file) => 0; + + // reset our timer + lfs3_kiwibd_simreset(CFG); + + // open the file + BENCH_START("read"); + lfs3_file_open(&lfs3, &file, "bench_linear", LFS3_O_RDONLY) => 0; + lfs3_off_t size = 0; + uint64_t readed = 0; + while (!(SIM_SIZE && readed >= (uint64_t)SIM_SIZE) + && !(SIM_TIME && lfs3_kiwibd_simtime(CFG) >= SIM_TIME)) { + // read from the file + uint8_t rbuf[CHUNK]; + lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; + + size += CHUNK; + readed += CHUNK; + + // rewind if we reach the end + if (size >= SIZE) { + lfs3_file_rewind(&lfs3, &file) => 0; + size = 0; + } + } + lfs3_file_close(&lfs3, &file) => 0; + // report the amount we managed to read + BENCH_STOP("read", readed); + + lfs3_unmount(&lfs3) => 0; +''' + +# random read throughput +[cases.bench_rt_random] +code = ''' + lfs3_t lfs3; + lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; + if (!SKIP_WARMUP) { + int err = bench_helpers_warmup(&lfs3); + if (err) { + return; + } + } + uint32_t prng = SEED; + + // reset our timer + lfs3_kiwibd_simreset(CFG); + + // create a file to read + lfs3_file_t file; + lfs3_file_open(&lfs3, &file, "bench_random", + LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; + for (lfs3_size_t i = 0; i < (SIZE+(CHUNK-1))/CHUNK; i++) { + uint8_t wbuf[CHUNK]; + for (lfs3_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (BENCH_PRNG(&prng) % 26); + } + lfs3_file_write(&lfs3, &file, wbuf, CHUNK) => CHUNK; + + // taking too long? + if (SIM_TIME && lfs3_kiwibd_simtime(CFG) >= SIM_TIME) { + return; + } + } + lfs3_file_close(&lfs3, &file) => 0; + + // reset our timer + lfs3_kiwibd_simreset(CFG); + + // open the file + BENCH_START("read"); + lfs3_file_open(&lfs3, &file, "bench_random", LFS3_O_RDONLY) => 0; + uint64_t readed = 0; + while (!(SIM_SIZE && readed >= (uint64_t)SIM_SIZE) + && !(SIM_TIME && lfs3_kiwibd_simtime(CFG) >= SIM_TIME)) { + // seek to a random location + lfs3_off_t pos = BENCH_PRNG(&prng) % SIZE; + lfs3_file_seek(&lfs3, &file, pos, LFS3_SEEK_SET) => pos; + + // read from the file + uint8_t rbuf[CHUNK]; + lfs3_ssize_t d = lfs3_file_read(&lfs3, &file, rbuf, CHUNK); + assert(d <= CHUNK); + + readed += CHUNK; + } + lfs3_file_close(&lfs3, &file) => 0; + // report the amount we managed to read + BENCH_STOP("read", readed); + + lfs3_unmount(&lfs3) => 0; +''' + +# many small file read throughput +[cases.bench_rt_many] +defines.FILE_SIZE = 'CHUNK' +defines.FILE_COUNT = '(SIZE+(FILE_SIZE-1)) / lfs3_max(FILE_SIZE, 1)' +code = ''' + lfs3_t lfs3; + lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; + lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; + if (!SKIP_WARMUP) { + int err = bench_helpers_warmup(&lfs3); + if (err) { + return; + } + } + uint32_t prng = SEED; + + // reset our timer + lfs3_kiwibd_simreset(CFG); + + // create files to read + for (lfs3_size_t i = 0; i < (SIZE+(CHUNK-1))/CHUNK; i++) { + char name[256]; + sprintf(name, "bench_%08x", i); + lfs3_file_t file; + lfs3_file_open(&lfs3, &file, name, + LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; + for (lfs3_size_t i = 0; i < (FILE_SIZE+(CHUNK-1))/CHUNK; i++) { + lfs3_ssize_t d = lfs3_min(CHUNK, FILE_SIZE); + uint8_t wbuf[CHUNK]; + memset(wbuf, 'a'+(BENCH_PRNG(&prng) % 26), d); + lfs3_file_write(&lfs3, &file, wbuf, d) => d; + + // taking too long? + if (SIM_TIME && lfs3_kiwibd_simtime(CFG) >= SIM_TIME) { + return; + } + } + lfs3_file_close(&lfs3, &file) => 0; + } + + // reset our timer + lfs3_kiwibd_simreset(CFG); + + // open the files + BENCH_START("read"); + uint64_t readed = 0; + while (!(SIM_SIZE && readed >= (uint64_t)SIM_SIZE) + && !(SIM_TIME && lfs3_kiwibd_simtime(CFG) >= SIM_TIME)) { + // choose a random filename + lfs3_off_t pos = BENCH_PRNG(&prng) % ((SIZE+(CHUNK-1))/CHUNK); + char name[256]; + sprintf(name, "bench_%08x", pos); + uint8_t rbuf[CHUNK]; + + // read the file + // + // note file == CHUNK here, the sum of all files should add up + // roughly to the benchmark SIZE + lfs3_file_t file; + lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0; + for (lfs3_size_t i = 0; i < (FILE_SIZE+(CHUNK-1))/CHUNK; i++) { + lfs3_ssize_t d = lfs3_min(CHUNK, FILE_SIZE); + lfs3_file_read(&lfs3, &file, rbuf, d) => d; + readed += d; + + // taking too long? + if (SIM_TIME && lfs3_kiwibd_simtime(CFG) >= SIM_TIME) { + break; + } + } + lfs3_file_close(&lfs3, &file) => 0; + } + // report the amount we managed to read + BENCH_STOP("read", readed); + + lfs3_unmount(&lfs3) => 0; +''' + diff --git a/benches/bench_wt.toml b/benches/bench_wt.toml index e37814e7..74ec7c56 100644 --- a/benches/bench_wt.toml +++ b/benches/bench_wt.toml @@ -1,4 +1,5 @@ # High-level write-throughput benchmarks +after = ['bench_file', 'bench_dir'] # these are common and can be overridden suite-wide # @@ -9,10 +10,6 @@ defines.SIZE = '1024*1024' # 1 MiB defines.CHUNK = 64 defines.SEED = 42 -# note FILE_SIZE is bench_*_many specific -defines.FILE_SIZE = 'CHUNK' -defines.FILE_COUNT = '(SIZE+(FILE_SIZE-1)) / lfs3_max(FILE_SIZE, 1)' - # simulated time, in nanoseconds, to run the bench defines.SIM_TIME = '60ULL*60ULL*1000ULL*1000ULL*1000ULL' # 1 hour # simulation size in bytes @@ -21,15 +18,12 @@ defines.SIM_SIZE = 0 # set this to true to skip bench warmup defines.SKIP_WARMUP = false -# don't fruncate, this is logging specific -defines.NO_FRUNCATE = 0 - - # include common bench helpers code = ''' -#include "benches/bench_helpers.h" + #include "benches/bench_helpers.h" ''' + # sequential write throughput [cases.bench_wt_seq] code = ''' @@ -164,6 +158,7 @@ code = ''' # 1. fruncate/rotations instead of rewind+truncate # 2. sync called on every write [cases.bench_wt_logging] +defines.NO_FRUNCATE = false code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; @@ -239,6 +234,8 @@ code = ''' # many small file write throughput [cases.bench_wt_many] +defines.FILE_SIZE = 'CHUNK' +defines.FILE_COUNT = '(SIZE+(FILE_SIZE-1)) / lfs3_max(FILE_SIZE, 1)' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;