Generalized btree benchmarks, amortized benchmarks, plot.py/plotmpl.py tweaks
These benchmarks are now more useful for seeing how these B-trees perform. In plot.py/plotmpl.py: - Added --legend as another alias for -l, --legend-right. - Allowed omitting of datasets from the legend by using empty strings in --labels. - Do not sum multiple data points on the same x coordinate. This was a bad idea that risks invalid results going unnoticed. As a plus multiple data points on the same x coordinate can be abused for a cheap representation of measurement error.
This commit is contained in:
@@ -5,6 +5,11 @@ defines.LOOKAHEAD_SIZE = 'BLOCK_COUNT / 8'
|
|||||||
|
|
||||||
[cases.bench_btree_lookup]
|
[cases.bench_btree_lookup]
|
||||||
defines.N = [8, 16, 32, 64, 128, 256, 1024]
|
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
|
||||||
in = 'lfs.c'
|
in = 'lfs.c'
|
||||||
code = '''
|
code = '''
|
||||||
lfs_t lfs;
|
lfs_t lfs;
|
||||||
@@ -17,17 +22,22 @@ code = '''
|
|||||||
lfs.free.i = 0;
|
lfs.free.i = 0;
|
||||||
lfs_alloc_ack(&lfs);
|
lfs_alloc_ack(&lfs);
|
||||||
|
|
||||||
|
uint32_t prng = SEED;
|
||||||
|
|
||||||
// create a tree with N elements
|
// create a tree with N elements
|
||||||
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
||||||
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
||||||
for (lfs_size_t i = 0; i < N; i++) {
|
for (lfs_size_t i = 0; i < N; i++) {
|
||||||
lfsr_btree_push(&lfs, &btree, i, LFSR_TAG_INLINED, 1,
|
lfs_off_t i_
|
||||||
|
= (ORDER == 0) ? i
|
||||||
|
: (ORDER == 1) ? 0
|
||||||
|
: BENCH_PRNG(&prng) % (btree.weight+1);
|
||||||
|
lfsr_btree_push(&lfs, &btree, i_, LFSR_TAG_INLINED, 1,
|
||||||
&alphas[i % 26], 1) => 0;
|
&alphas[i % 26], 1) => 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// bench lookup
|
// bench lookup
|
||||||
BENCH_START();
|
BENCH_START();
|
||||||
uint32_t prng = 42;
|
|
||||||
lfs_size_t i = BENCH_PRNG(&prng) % N;
|
lfs_size_t i = BENCH_PRNG(&prng) % N;
|
||||||
uint8_t buffer[4];
|
uint8_t buffer[4];
|
||||||
lfsr_tag_t tag_;
|
lfsr_tag_t tag_;
|
||||||
@@ -40,12 +50,17 @@ code = '''
|
|||||||
assert(tag_ == LFSR_TAG_INLINED);
|
assert(tag_ == LFSR_TAG_INLINED);
|
||||||
assert(id_ == i);
|
assert(id_ == i);
|
||||||
assert(weight_ == 1);
|
assert(weight_ == 1);
|
||||||
assert(memcmp(buffer, &alphas[i % 26], 1) == 0);
|
|
||||||
BENCH_STOP();
|
BENCH_STOP();
|
||||||
'''
|
'''
|
||||||
|
|
||||||
[cases.bench_btree_append]
|
[cases.bench_btree_commit]
|
||||||
defines.N = [8, 16, 32, 64, 128, 256, 1024]
|
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.AMORTIZED = false
|
||||||
in = 'lfs.c'
|
in = 'lfs.c'
|
||||||
code = '''
|
code = '''
|
||||||
lfs_t lfs;
|
lfs_t lfs;
|
||||||
@@ -58,19 +73,28 @@ code = '''
|
|||||||
lfs.free.i = 0;
|
lfs.free.i = 0;
|
||||||
lfs_alloc_ack(&lfs);
|
lfs_alloc_ack(&lfs);
|
||||||
|
|
||||||
uint32_t prng = 42;
|
uint32_t prng = SEED;
|
||||||
|
|
||||||
// create a tree with N elements
|
// create a tree with N elements
|
||||||
|
if (AMORTIZED) {
|
||||||
|
BENCH_START();
|
||||||
|
}
|
||||||
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
lfsr_btree_t btree = LFSR_BTREE_NULL;
|
||||||
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
const char *alphas = "abcdefghijklmnopqrstuvwxyz";
|
||||||
for (lfs_size_t i = 0; i < N; i++) {
|
for (lfs_size_t i = 0; i < N; i++) {
|
||||||
lfsr_btree_push(&lfs, &btree, i, LFSR_TAG_INLINED, 1,
|
lfs_off_t i_
|
||||||
|
= (ORDER == 0) ? i
|
||||||
|
: (ORDER == 1) ? 0
|
||||||
|
: BENCH_PRNG(&prng) % (btree.weight+1);
|
||||||
|
lfsr_btree_push(&lfs, &btree, i_, LFSR_TAG_INLINED, 1,
|
||||||
&alphas[i % 26], 1) => 0;
|
&alphas[i % 26], 1) => 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// bench appending a new id
|
// bench appending a new id
|
||||||
BENCH_START();
|
if (!AMORTIZED) {
|
||||||
lfs_size_t i = N;
|
BENCH_START();
|
||||||
|
}
|
||||||
|
lfs_size_t i = BENCH_PRNG(&prng) % N;
|
||||||
lfsr_btree_push(&lfs, &btree, i, LFSR_TAG_INLINED, 1,
|
lfsr_btree_push(&lfs, &btree, i, LFSR_TAG_INLINED, 1,
|
||||||
&alphas[i % 26], 1) => 0;
|
&alphas[i % 26], 1) => 0;
|
||||||
BENCH_STOP();
|
BENCH_STOP();
|
||||||
@@ -86,5 +110,5 @@ code = '''
|
|||||||
assert(tag_ == LFSR_TAG_INLINED);
|
assert(tag_ == LFSR_TAG_INLINED);
|
||||||
assert(id_ == i);
|
assert(id_ == i);
|
||||||
assert(weight_ == 1);
|
assert(weight_ == 1);
|
||||||
assert(memcmp(buffer, &alphas[i % 26], 1) == 0);
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|||||||
+34
-36
@@ -468,7 +468,7 @@ def collect(csv_paths, renames=[]):
|
|||||||
|
|
||||||
def dataset(results, x=None, y=None, define=[]):
|
def dataset(results, x=None, y=None, define=[]):
|
||||||
# organize by 'by', x, and y
|
# organize by 'by', x, and y
|
||||||
dataset = {}
|
dataset = []
|
||||||
i = 0
|
i = 0
|
||||||
for r in results:
|
for r in results:
|
||||||
# filter results by matching defines
|
# filter results by matching defines
|
||||||
@@ -498,10 +498,7 @@ def dataset(results, x=None, y=None, define=[]):
|
|||||||
else:
|
else:
|
||||||
y_ = None
|
y_ = None
|
||||||
|
|
||||||
if y_ is not None:
|
dataset.append((x_, y_))
|
||||||
dataset[x_] = y_ + dataset.get(x_, 0)
|
|
||||||
else:
|
|
||||||
dataset[x_] = y_ or dataset.get(x_, None)
|
|
||||||
|
|
||||||
return dataset
|
return dataset
|
||||||
|
|
||||||
@@ -880,7 +877,7 @@ def main(csv_paths, *,
|
|||||||
if labels is not None:
|
if labels is not None:
|
||||||
labels_ = labels
|
labels_ = labels
|
||||||
else:
|
else:
|
||||||
labels_ = ['']
|
labels_ = [None]
|
||||||
|
|
||||||
# allow escape codes in labels/titles
|
# allow escape codes in labels/titles
|
||||||
title = escape(title).splitlines() if title is not None else []
|
title = escape(title).splitlines() if title is not None else []
|
||||||
@@ -1018,6 +1015,8 @@ def main(csv_paths, *,
|
|||||||
if legend_right or legend_above or legend_below:
|
if legend_right or legend_above or legend_below:
|
||||||
legend_ = []
|
legend_ = []
|
||||||
for i, k in enumerate(datasets_.keys()):
|
for i, k in enumerate(datasets_.keys()):
|
||||||
|
if datalabels_[k] is not None and not datalabels_[k]:
|
||||||
|
continue
|
||||||
label = '%s%s' % (
|
label = '%s%s' % (
|
||||||
'%s ' % datachars_[k]
|
'%s ' % datachars_[k]
|
||||||
if chars is not None
|
if chars is not None
|
||||||
@@ -1028,7 +1027,7 @@ def main(csv_paths, *,
|
|||||||
or ','.join(k_ for k_ in k if k_))
|
or ','.join(k_ for k_ in k if k_))
|
||||||
|
|
||||||
if label:
|
if label:
|
||||||
legend_.append(label)
|
legend_.append((label, colors_[i % len(colors_)]))
|
||||||
legend_width = max(legend_width, len(label)+1)
|
legend_width = max(legend_width, len(label)+1)
|
||||||
|
|
||||||
# figure out our canvas size
|
# figure out our canvas size
|
||||||
@@ -1064,7 +1063,7 @@ def main(csv_paths, *,
|
|||||||
legend_cols = len(legend_)
|
legend_cols = len(legend_)
|
||||||
while True:
|
while True:
|
||||||
legend_widths = [
|
legend_widths = [
|
||||||
max(len(l) for l in legend_[i::legend_cols])
|
max(len(l) for l, _ in legend_[i::legend_cols])
|
||||||
for i in range(legend_cols)]
|
for i in range(legend_cols)]
|
||||||
if (legend_cols <= 1
|
if (legend_cols <= 1
|
||||||
or sum(legend_widths)+2*(legend_cols-1)
|
or sum(legend_widths)+2*(legend_cols-1)
|
||||||
@@ -1077,7 +1076,7 @@ def main(csv_paths, *,
|
|||||||
legend_cols = len(legend_)
|
legend_cols = len(legend_)
|
||||||
while True:
|
while True:
|
||||||
legend_widths = [
|
legend_widths = [
|
||||||
max(len(l) for l in legend_[i::legend_cols])
|
max(len(l) for l, _ in legend_[i::legend_cols])
|
||||||
for i in range(legend_cols)]
|
for i in range(legend_cols)]
|
||||||
if (legend_cols <= 1
|
if (legend_cols <= 1
|
||||||
or sum(legend_widths)+2*(legend_cols-1)
|
or sum(legend_widths)+2*(legend_cols-1)
|
||||||
@@ -1151,27 +1150,27 @@ def main(csv_paths, *,
|
|||||||
# find actual xlim/ylim
|
# find actual xlim/ylim
|
||||||
xlim_ = (
|
xlim_ = (
|
||||||
xlim_[0] if xlim_[0] is not None
|
xlim_[0] if xlim_[0] is not None
|
||||||
else min(it.chain([0], (k
|
else min(it.chain([0], (x
|
||||||
for r in subdatasets.values()
|
for dataset in subdatasets.values()
|
||||||
for k, v in r.items()
|
for x, y in dataset
|
||||||
if v is not None))),
|
if y is not None))),
|
||||||
xlim_[1] if xlim_[1] is not None
|
xlim_[1] if xlim_[1] is not None
|
||||||
else max(it.chain([0], (k
|
else max(it.chain([0], (x
|
||||||
for r in subdatasets.values()
|
for dataset in subdatasets.values()
|
||||||
for k, v in r.items()
|
for x, y in dataset
|
||||||
if v is not None))))
|
if y is not None))))
|
||||||
|
|
||||||
ylim_ = (
|
ylim_ = (
|
||||||
ylim_[0] if ylim_[0] is not None
|
ylim_[0] if ylim_[0] is not None
|
||||||
else min(it.chain([0], (v
|
else min(it.chain([0], (y
|
||||||
for r in subdatasets.values()
|
for dataset in subdatasets.values()
|
||||||
for _, v in r.items()
|
for _, y in dataset
|
||||||
if v is not None))),
|
if y is not None))),
|
||||||
ylim_[1] if ylim_[1] is not None
|
ylim_[1] if ylim_[1] is not None
|
||||||
else max(it.chain([0], (v
|
else max(it.chain([0], (y
|
||||||
for r in subdatasets.values()
|
for dataset in subdatasets.values()
|
||||||
for _, v in r.items()
|
for _, y in dataset
|
||||||
if v is not None))))
|
if y is not None))))
|
||||||
|
|
||||||
# find actual width/height
|
# find actual width/height
|
||||||
subwidth = sum(widths[s.x:s.x+s.xspan]) - sum(s.xmargin)
|
subwidth = sum(widths[s.x:s.x+s.xspan]) - sum(s.xmargin)
|
||||||
@@ -1190,7 +1189,7 @@ def main(csv_paths, *,
|
|||||||
|
|
||||||
for name, dataset in subdatasets.items():
|
for name, dataset in subdatasets.items():
|
||||||
plot.plot(
|
plot.plot(
|
||||||
sorted((x,y) for x,y in dataset.items()),
|
sorted((x,y) for x,y in dataset),
|
||||||
color=datacolors_[name],
|
color=datacolors_[name],
|
||||||
char=datachars_[name],
|
char=datachars_[name],
|
||||||
line_char=dataline_chars_[name])
|
line_char=dataline_chars_[name])
|
||||||
@@ -1230,11 +1229,9 @@ def main(csv_paths, *,
|
|||||||
// 2,
|
// 2,
|
||||||
0), '',
|
0), '',
|
||||||
' '.join('%s%s%s' % (
|
' '.join('%s%s%s' % (
|
||||||
'\x1b[%sm' % colors_[(i+j) % len(colors_)]
|
'\x1b[%sm' % legend_[i+j][1] if color else '',
|
||||||
if color else '',
|
'%-*s' % (legend_widths[j], legend_[i+j][0]),
|
||||||
'%-*s' % (legend_widths[j], legend_[i+j]),
|
'\x1b[m' if color else '')
|
||||||
'\x1b[m'
|
|
||||||
if color else '')
|
|
||||||
for j in range(min(legend_cols, len(legend_)-i)))))
|
for j in range(min(legend_cols, len(legend_)-i)))))
|
||||||
|
|
||||||
for row in range(height_):
|
for row in range(height_):
|
||||||
@@ -1344,8 +1341,8 @@ def main(csv_paths, *,
|
|||||||
and row-ymargin[-1] < len(legend_)):
|
and row-ymargin[-1] < len(legend_)):
|
||||||
j = row-ymargin[-1]
|
j = row-ymargin[-1]
|
||||||
f.write(' %s%s%s' % (
|
f.write(' %s%s%s' % (
|
||||||
'\x1b[%sm' % colors_[j % len(colors_)] if color else '',
|
'\x1b[%sm' % legend_[j][1] if color else '',
|
||||||
legend_[j],
|
legend_[j][0],
|
||||||
'\x1b[m' if color else ''))
|
'\x1b[m' if color else ''))
|
||||||
|
|
||||||
f.writeln()
|
f.writeln()
|
||||||
@@ -1366,9 +1363,9 @@ def main(csv_paths, *,
|
|||||||
// 2,
|
// 2,
|
||||||
0), '',
|
0), '',
|
||||||
' '.join('%s%s%s' % (
|
' '.join('%s%s%s' % (
|
||||||
'\x1b[%sm' % colors_[(i+j) % len(colors_)]
|
'\x1b[%sm' % legend_[i+j][1]
|
||||||
if color else '',
|
if color else '',
|
||||||
'%-*s' % (legend_widths[j], legend_[i+j]),
|
'%-*s' % (legend_widths[j], legend_[i+j][0]),
|
||||||
'\x1b[m'
|
'\x1b[m'
|
||||||
if color else '')
|
if color else '')
|
||||||
for j in range(min(legend_cols, len(legend_)-i)))))
|
for j in range(min(legend_cols, len(legend_)-i)))))
|
||||||
@@ -1552,7 +1549,8 @@ if __name__ == "__main__":
|
|||||||
'-t', '--title',
|
'-t', '--title',
|
||||||
help="Add a title.")
|
help="Add a title.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-l', '--legend-right',
|
'-l', '--legend', '--legend-right',
|
||||||
|
dest='legend_right',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help="Place a legend to the right.")
|
help="Place a legend to the right.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
|||||||
+25
-24
@@ -214,7 +214,7 @@ def collect(csv_paths, renames=[]):
|
|||||||
|
|
||||||
def dataset(results, x=None, y=None, define=[]):
|
def dataset(results, x=None, y=None, define=[]):
|
||||||
# organize by 'by', x, and y
|
# organize by 'by', x, and y
|
||||||
dataset = {}
|
dataset = []
|
||||||
i = 0
|
i = 0
|
||||||
for r in results:
|
for r in results:
|
||||||
# filter results by matching defines
|
# filter results by matching defines
|
||||||
@@ -244,10 +244,7 @@ def dataset(results, x=None, y=None, define=[]):
|
|||||||
else:
|
else:
|
||||||
y_ = None
|
y_ = None
|
||||||
|
|
||||||
if y_ is not None:
|
dataset.append((x_, y_))
|
||||||
dataset[x_] = y_ + dataset.get(x_, 0)
|
|
||||||
else:
|
|
||||||
dataset[x_] = y_ or dataset.get(x_, None)
|
|
||||||
|
|
||||||
return dataset
|
return dataset
|
||||||
|
|
||||||
@@ -649,7 +646,7 @@ def main(csv_paths, output, *,
|
|||||||
if labels is not None:
|
if labels is not None:
|
||||||
labels_ = labels
|
labels_ = labels
|
||||||
else:
|
else:
|
||||||
labels_ = ['']
|
labels_ = [None]
|
||||||
|
|
||||||
if font_color is not None:
|
if font_color is not None:
|
||||||
font_color_ = font_color
|
font_color_ = font_color
|
||||||
@@ -844,7 +841,7 @@ def main(csv_paths, output, *,
|
|||||||
# plot!
|
# plot!
|
||||||
ax = s.ax
|
ax = s.ax
|
||||||
for name, dataset in subdatasets.items():
|
for name, dataset in subdatasets.items():
|
||||||
dats = sorted((x,y) for x,y in dataset.items())
|
dats = sorted((x,y) for x,y in dataset)
|
||||||
ax.plot([x for x,_ in dats], [y for _,y in dats],
|
ax.plot([x for x,_ in dats], [y for _,y in dats],
|
||||||
dataformats_[name],
|
dataformats_[name],
|
||||||
color=datacolors_[name],
|
color=datacolors_[name],
|
||||||
@@ -860,26 +857,26 @@ def main(csv_paths, output, *,
|
|||||||
# axes limits
|
# axes limits
|
||||||
ax.set_xlim(
|
ax.set_xlim(
|
||||||
xlim_[0] if xlim_[0] is not None
|
xlim_[0] if xlim_[0] is not None
|
||||||
else min(it.chain([0], (k
|
else min(it.chain([0], (x
|
||||||
for r in subdatasets.values()
|
for dataset in subdatasets.values()
|
||||||
for k, v in r.items()
|
for x, y in dataset
|
||||||
if v is not None))),
|
if y is not None))),
|
||||||
xlim_[1] if xlim_[1] is not None
|
xlim_[1] if xlim_[1] is not None
|
||||||
else max(it.chain([0], (k
|
else max(it.chain([0], (x
|
||||||
for r in subdatasets.values()
|
for r in subdatasets.values()
|
||||||
for k, v in r.items()
|
for x, y in dataset
|
||||||
if v is not None))))
|
if y is not None))))
|
||||||
ax.set_ylim(
|
ax.set_ylim(
|
||||||
ylim_[0] if ylim_[0] is not None
|
ylim_[0] if ylim_[0] is not None
|
||||||
else min(it.chain([0], (v
|
else min(it.chain([0], (y
|
||||||
for r in subdatasets.values()
|
for dataset in subdatasets.values()
|
||||||
for _, v in r.items()
|
for _, y in dataset
|
||||||
if v is not None))),
|
if y is not None))),
|
||||||
ylim_[1] if ylim_[1] is not None
|
ylim_[1] if ylim_[1] is not None
|
||||||
else max(it.chain([0], (v
|
else max(it.chain([0], (y
|
||||||
for r in subdatasets.values()
|
for dataset in subdatasets.values()
|
||||||
for _, v in r.items()
|
for _, y in dataset
|
||||||
if v is not None))))
|
if y is not None))))
|
||||||
# axes ticks
|
# axes ticks
|
||||||
if x2_:
|
if x2_:
|
||||||
ax.xaxis.set_major_formatter(lambda x, pos:
|
ax.xaxis.set_major_formatter(lambda x, pos:
|
||||||
@@ -960,7 +957,10 @@ def main(csv_paths, output, *,
|
|||||||
for name in datasets_.keys():
|
for name in datasets_.keys():
|
||||||
name_ = ','.join(k for k in name if k)
|
name_ = ','.join(k for k in name if k)
|
||||||
if name_ in legend:
|
if name_ in legend:
|
||||||
legend_.append((datalabels_[name] or name_, legend[name_]))
|
if datalabels_[name] is None:
|
||||||
|
legend_.append((name_, legend[name_]))
|
||||||
|
elif datalabels_[name]:
|
||||||
|
legend_.append((datalabels_[name], legend[name_]))
|
||||||
legend = legend_
|
legend = legend_
|
||||||
|
|
||||||
if legend_right:
|
if legend_right:
|
||||||
@@ -1226,7 +1226,8 @@ if __name__ == "__main__":
|
|||||||
'-t', '--title',
|
'-t', '--title',
|
||||||
help="Add a title.")
|
help="Add a title.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-l', '--legend-right',
|
'-l', '--legend', '--legend-right',
|
||||||
|
dest='legend_right',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help="Place a legend to the right.")
|
help="Place a legend to the right.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
|||||||
Reference in New Issue
Block a user