Reworked btree-commit/flushbuffer to incrementally build attrs

This basically turns these functions into tiny bounded compilers, which
is interesting to think about. I wonder if this sort of evolution led to
how queries are compiled in modern databases.

This method of attr generation is both easier to use and more flexible.

It also saves some code, but note lfsr_file_flushbuffer underwent
significant tweaking leveraging this, so the actual code savings are a
bit muddy:

            code          stack
  before:  25672           2024
  after:   25452 (-0.9%)   1920 (-5.4%)
This commit is contained in:
Christopher Haster
2023-09-24 15:18:02 -05:00
parent dc8dce8f0c
commit c2d33a1843
+156 -204
View File
@@ -1027,12 +1027,11 @@ static lfs_ssize_t lfsr_bd_progtag(lfs_t *lfs,
((lfsr_data_t){.u.direct.buffer=(const void*)(lfsr_grm_t*){_grm}}) ((lfsr_data_t){.u.direct.buffer=(const void*)(lfsr_grm_t*){_grm}})
// writing to an unrelated trunk in the rbyd // writing to an unrelated trunk in the rbyd
#define LFSR_DATA_SHRUBATTRS(_file, ...) \ #define LFSR_DATA_SHRUBATTRS(_file, _attrs, _attr_count) \
((lfsr_data_t){.u.direct.buffer=(const void*)&(const lfsr_shrubattrs_t){ \ ((lfsr_data_t){.u.direct.buffer=(const void*)&(const lfsr_shrubattrs_t){ \
.file=_file, \ .file=_file, \
.attrs=(const lfsr_attr_t[]){__VA_ARGS__}, \ .attrs=_attrs, \
.attr_count=sizeof((const lfsr_attr_t[]){__VA_ARGS__}) \ .attr_count=_attr_count}})
/ sizeof(lfsr_attr_t)}})
// the reason for lazily encoding inlined trunks is because they can change // the reason for lazily encoding inlined trunks is because they can change
// underneath us during mdir compaction, the horror // underneath us during mdir compaction, the horror
@@ -4338,27 +4337,25 @@ static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree,
bid -= rid - (rbyd.weight-1); bid -= rid - (rbyd.weight-1);
LFS_ASSERT(rbyd_.weight > 0); LFS_ASSERT(rbyd_.weight > 0);
LFS_ASSERT(sibling.weight > 0); LFS_ASSERT(sibling.weight > 0);
lfsr_attr_t *attrs_ = scratch_attrs;
if (rbyd.weight == 0) { if (rbyd.weight == 0) {
scratch_attrs[0] = LFSR_ATTR(bid, *attrs_++ = LFSR_ATTR(bid,
BTREE, +rbyd_.weight, FROMBTREE(&rbyd_, scratch_buf)); BTREE, +rbyd_.weight, FROMBTREE(&rbyd_, scratch_buf));
scratch_attrs[1] = LFSR_ATTR_NOOP;
} else { } else {
scratch_attrs[0] = LFSR_ATTR(bid+rid, *attrs_++ = LFSR_ATTR(bid+rid,
BTREE, 0, FROMBTREE(&rbyd_, scratch_buf)); BTREE, 0, FROMBTREE(&rbyd_, scratch_buf));
scratch_attrs[1] = LFSR_ATTR(bid+rid, *attrs_++ = LFSR_ATTR(bid+rid,
GROW, -rbyd.weight + rbyd_.weight, NULL); GROW, -rbyd.weight + rbyd_.weight, NULL);
} }
scratch_attrs[2] = LFSR_ATTR(bid+rid - rbyd.weight + rbyd_.weight + 1, *attrs_++ = LFSR_ATTR(bid+rid - rbyd.weight + rbyd_.weight + 1,
BTREE, +sibling.weight, FROMBTREE(&sibling, scratch_buf_)); BTREE, +sibling.weight, FROMBTREE(&sibling, scratch_buf_));
if (lfsr_tag_suptype(split_tag) == LFSR_TAG_NAME) { if (lfsr_tag_suptype(split_tag) == LFSR_TAG_NAME) {
scratch_attrs[3] = LFSR_ATTR( *attrs_++ = LFSR_ATTR(
bid+rid - rbyd.weight + rbyd_.weight + sibling.weight, bid+rid - rbyd.weight + rbyd_.weight + sibling.weight,
BRANCH, 0, DATA(split_data)); BRANCH, 0, DATA(split_data));
} else {
scratch_attrs[3] = LFSR_ATTR_NOOP;
} }
attrs = scratch_attrs; attrs = scratch_attrs;
attr_count = 4; attr_count = attrs_ - scratch_attrs;
rbyd = parent; rbyd = parent;
continue; continue;
@@ -4444,14 +4441,15 @@ static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree,
// prepare commit to parent, tail recursing upwards // prepare commit to parent, tail recursing upwards
bid -= rid - (rbyd.weight-1); bid -= rid - (rbyd.weight-1);
LFS_ASSERT(rbyd_.weight > 0); LFS_ASSERT(rbyd_.weight > 0);
scratch_attrs[0] = LFSR_ATTR(bid+rid+sibling.weight, attrs_ = scratch_attrs;
*attrs_++ = LFSR_ATTR(bid+rid+sibling.weight,
RM, -sibling.weight, NULL); RM, -sibling.weight, NULL);
scratch_attrs[1] = LFSR_ATTR(bid+rid, *attrs_++ = LFSR_ATTR(bid+rid,
BTREE, 0, FROMBTREE(&rbyd_, scratch_buf)); BTREE, 0, FROMBTREE(&rbyd_, scratch_buf));
scratch_attrs[2] = LFSR_ATTR(bid+rid, *attrs_++ = LFSR_ATTR(bid+rid,
GROW, -rbyd.weight + rbyd_.weight, NULL); GROW, -rbyd.weight + rbyd_.weight, NULL);
attrs = scratch_attrs; attrs = scratch_attrs;
attr_count = 3; attr_count = attrs_ - scratch_attrs;
rbyd = parent; rbyd = parent;
continue; continue;
@@ -4476,19 +4474,18 @@ static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree,
// note that since we defer merges to compaction time, we can // note that since we defer merges to compaction time, we can
// end up removing an rbyd here // end up removing an rbyd here
bid -= rid - (rbyd.weight-1); bid -= rid - (rbyd.weight-1);
attrs_ = scratch_attrs;
if (rbyd_.weight == 0) { if (rbyd_.weight == 0) {
scratch_attrs[0] = LFSR_ATTR(bid+rid, *attrs_++ = LFSR_ATTR(bid+rid,
RM, -rbyd.weight, NULL); RM, -rbyd.weight, NULL);
attrs = scratch_attrs;
attr_count = 1;
} else { } else {
scratch_attrs[0] = LFSR_ATTR(bid+rid, *attrs_++ = LFSR_ATTR(bid+rid,
BTREE, 0, FROMBTREE(&rbyd_, scratch_buf)); BTREE, 0, FROMBTREE(&rbyd_, scratch_buf));
scratch_attrs[1] = LFSR_ATTR(bid+rid, *attrs_++ = LFSR_ATTR(bid+rid,
GROW, -rbyd.weight + rbyd_.weight, NULL); GROW, -rbyd.weight + rbyd_.weight, NULL);
attrs = scratch_attrs;
attr_count = 2;
} }
attrs = scratch_attrs;
attr_count = attrs_ - scratch_attrs;
rbyd = parent; rbyd = parent;
} }
@@ -8371,42 +8368,50 @@ lfs_ssize_t lfsr_file_read(lfs_t *lfs, lfsr_file_t *file,
static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) { static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) {
while (file->buffer_size > 0) { while (file->buffer_size > 0) {
// figure out how to flush // figure out how to flush
lfsr_attr_t hole_attr = LFSR_ATTR_NOOP; lfsr_attr_t scratch_attrs[5];
lfsr_attr_t *attrs_ = scratch_attrs;
lfs_off_t pos = file->buffer_pos; lfs_off_t pos = file->buffer_pos;
lfs_off_t weight = file->buffer_size; lfs_off_t weight = 0;
lfsr_data_t datas[3] = { lfsr_data_t scratch_datas[3];
LFSR_DATA_NULL, lfsr_data_t *datas_ = scratch_datas;
LFSR_DATA_BUF(file->buffer, file->buffer_size),
LFSR_DATA_NULL,
};
// have inlined data? // have inlined data?
if (!lfsr_file_hasshrub(file)) { if (!lfsr_file_hasshrub(file)) {
// merge/carve inlined data and our buffer? // need a hole?
if (lfsr_data_size(&file->inlined.u.data) >= file->buffer_pos) { if (file->buffer_pos > lfsr_data_size(&file->inlined.u.data)) {
pos = 0; *attrs_++ = LFSR_ATTR(0,
weight = lfs_max32(
file->buffer_pos + file->buffer_size,
lfsr_data_size(&file->inlined.u.data));
datas[0] = LFSR_DATA_DISK(
file->inlined.u.data.u.disk.block,
file->inlined.u.data.u.disk.off,
lfs_min32(
lfsr_data_size(&file->inlined.u.data),
file->buffer_pos));
datas[2] = LFSR_DATA_DISK(
file->inlined.u.data.u.disk.block,
file->inlined.u.data.u.disk.off
+ file->buffer_pos + file->buffer_size,
lfsr_data_size(&file->inlined.u.data) - lfs_min32(
file->buffer_pos + file->buffer_size,
lfsr_data_size(&file->inlined.u.data)));
// we can't merge if we have a hole, create two-leaf shrub
} else {
hole_attr = LFSR_ATTR(0,
SHRUB(INLINED), +file->buffer_pos, DATA( SHRUB(INLINED), +file->buffer_pos, DATA(
file->inlined.u.data)); file->inlined.u.data));
// left data?
} else if (file->buffer_pos > 0) {
lfs_size_t left_size = file->buffer_pos;
pos = 0;
weight += left_size;
*datas_++ = LFSR_DATA_DISK(
file->inlined.u.data.u.disk.block,
file->inlined.u.data.u.disk.off,
left_size);
}
// buffer data?
*datas_++ = LFSR_DATA_BUF(file->buffer, file->buffer_size);
weight += file->buffer_size;
// right data?
if (lfsr_data_size(&file->inlined.u.data)
> file->buffer_pos + file->buffer_size) {
lfs_size_t right_size
= lfsr_data_size(&file->inlined.u.data)
- (file->buffer_pos + file->buffer_size);
weight += right_size;
*datas_++ = LFSR_DATA_DISK(
file->inlined.u.data.u.disk.block,
file->inlined.u.data.u.disk.off
+ lfsr_data_size(&file->inlined.u.data)
- right_size,
right_size);
} }
// have a shrub? // have a shrub?
@@ -8415,171 +8420,118 @@ static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) {
// should revert to an inlined file // should revert to an inlined file
LFS_ASSERT(file->inlined.u.rbyd.weight > 0); LFS_ASSERT(file->inlined.u.rbyd.weight > 0);
// find left sibling // left sibling?
lfsr_srid_t left_rid; if (file->buffer_pos > 0) {
lfsr_tag_t left_tag; lfsr_srid_t left_rid;
lfsr_rid_t left_weight; lfsr_tag_t left_tag;
lfsr_data_t left_data; lfsr_rid_t left_weight;
int err = lfsr_rbyd_lookupnext(lfs, &file->inlined.u.rbyd, lfsr_data_t left_data;
lfs_min32( int err = lfsr_rbyd_lookupnext(lfs, &file->inlined.u.rbyd,
file->buffer_pos, lfs_min32(
file->inlined.u.rbyd.weight)-1, 0, file->buffer_pos,
&left_rid, &left_tag, &left_weight, &left_data); file->inlined.u.rbyd.weight)-1, 0,
if (err && err != LFS_ERR_NOENT) { &left_rid, &left_tag, &left_weight, &left_data);
return err; if (err && err != LFS_ERR_NOENT) {
} return err;
LFS_ASSERT(err != LFS_ERR_NOENT); }
LFS_ASSERT(left_tag == LFSR_TAG_SHRUB(INLINED)); LFS_ASSERT(err != LFS_ERR_NOENT);
LFS_ASSERT(left_tag == LFSR_TAG_SHRUB(INLINED));
// left sibling overlaps? need to merge/carve // need to grow/shrink a hole?
// left sibling touches? only merge if we won't end up if (file->buffer_pos != (lfs_off_t)left_rid+1
// carving later && file->buffer_pos
lfs_soff_t left_overlap >= left_rid-(left_weight-1)
= (left_rid-(left_weight-1) + lfsr_data_size(&left_data)) + lfsr_data_size(&left_data)) {
- file->buffer_pos; *attrs_++ = LFSR_ATTR(left_rid,
if (left_overlap > 0 GROW, +file->buffer_pos - (left_rid+1), NULL);
|| (left_overlap == 0
&& weight + lfsr_data_size(&left_data) // left data?
<= lfs->cfg->coalesce_size)) { } else if (file->buffer_pos
pos = left_rid - (left_weight-1); < left_rid-(left_weight-1)
weight += file->buffer_pos - pos; + lfsr_data_size(&left_data)) {
datas[0] = LFSR_DATA_DISK( lfs_size_t left_size
left_data.u.disk.block, = file->buffer_pos
left_data.u.disk.off, - (left_rid-(left_weight-1));
file->buffer_pos - pos); pos = left_rid-(left_weight-1);
} else { weight += left_size;
// need a hole? *datas_++ = LFSR_DATA_DISK(
if (left_overlap < 0) { left_data.u.disk.block,
hole_attr = LFSR_ATTR(left_rid, GROW, -left_overlap, NULL); left_data.u.disk.off,
left_size);
} }
} }
// find right sibling // buffer data?
lfsr_srid_t right_rid; *datas_++ = LFSR_DATA_BUF(file->buffer, file->buffer_size);
lfsr_tag_t right_tag; weight += file->buffer_size;
lfsr_rid_t right_weight;
lfsr_data_t right_data;
err = lfsr_rbyd_lookupnext(lfs, &file->inlined.u.rbyd,
file->buffer_pos + file->buffer_size, 0,
&right_rid, &right_tag, &right_weight, &right_data);
if (err && err != LFS_ERR_NOENT) {
return err;
}
LFS_ASSERT(err == LFS_ERR_NOENT
|| right_tag == LFSR_TAG_SHRUB(INLINED));
if (err != LFS_ERR_NOENT) { // right sibling?
// right sibling overlaps? need to merge/carve if (file->buffer_pos + file->buffer_size
// right sibling touches? only merge if we won't end up < (lfs_off_t)file->inlined.u.rbyd.weight) {
// carving later lfsr_srid_t right_rid;
lfs_soff_t right_overlap lfsr_tag_t right_tag;
= (file->buffer_pos + file->buffer_size) lfsr_rid_t right_weight;
- (right_rid - (right_weight-1)); lfsr_data_t right_data;
LFS_ASSERT(right_overlap >= 0); int err = lfsr_rbyd_lookupnext(lfs, &file->inlined.u.rbyd,
if (right_overlap > 0 file->buffer_pos + file->buffer_size, 0,
|| (right_overlap == 0 &right_rid, &right_tag, &right_weight, &right_data);
&& weight + lfsr_data_size(&right_data) if (err && err != LFS_ERR_NOENT) {
<= lfs->cfg->coalesce_size)) { return err;
// note physical right data size risks going negative here }
// because of holes LFS_ASSERT(err != LFS_ERR_NOENT);
weight += right_weight - right_overlap; LFS_ASSERT(right_tag == LFSR_TAG_SHRUB(INLINED));
datas[2] = LFSR_DATA_DISK(
// right data?
if (right_rid-(right_weight-1) + lfsr_data_size(&right_data)
> file->buffer_pos + file->buffer_size) {
lfs_size_t right_size
= (right_rid-(right_weight-1)
+ lfsr_data_size(&right_data))
- (file->buffer_pos + file->buffer_size);
*datas_++ = LFSR_DATA_DISK(
right_data.u.disk.block, right_data.u.disk.block,
right_data.u.disk.off + right_overlap, right_data.u.disk.off
lfsr_data_size(&right_data) - lfs_min32( + lfsr_data_size(&right_data)
right_overlap, - right_size,
lfsr_data_size(&right_data))); right_size);
} }
// need to bring over a hole?
weight += right_rid+1 - (file->buffer_pos + file->buffer_size);
} }
// make sure we remove any data we're overwriting
*attrs_++ = LFSR_ATTR(
lfs_min32(
pos + weight,
file->inlined.u.rbyd.weight)-1,
RM,
-(lfs_min32(
pos + weight,
file->inlined.u.rbyd.weight)
- pos),
NULL);
} }
// can we coalesce any of our data? // compile our data references into attributes
// TODO a better way to do this? const lfsr_data_t *datas = scratch_datas;
lfsr_attr_t coalesce_attrs[3] = { lfs_size_t data_count = datas_ - scratch_datas;
LFSR_ATTR_NOOP, for (lfs_size_t i = 0; i < data_count; i++) {
LFSR_ATTR_NOOP, *attrs_++ = LFSR_ATTR(pos,
LFSR_ATTR_NOOP, // include the remaining weight if we are the last data
}; SHRUB(INLINED), +(i == data_count-1
if (lfsr_data_size(&datas[0]) ? weight
+ lfsr_data_size(&datas[1]) : lfsr_data_size(&datas[i])),
+ lfsr_data_size(&datas[2]) DATA(datas[i]));
// make sure to never write null siblings, even when pos += lfsr_data_size(&datas[i]);
// buffer > cache size weight -= lfsr_data_size(&datas[i]);
<= lfs_max32(lfs->cfg->coalesce_size, file->buffer_size)) {
coalesce_attrs[0] = LFSR_ATTR(pos,
SHRUB(INLINED),
+weight,
DATA(lfsr_data_fromcat(datas, 3)));
} else if (lfsr_data_size(&datas[0])
+ lfsr_data_size(&datas[1])
// make sure to never write null siblings, even when
// buffer > cache size
<= lfs_max32(lfs->cfg->coalesce_size, file->buffer_size)) {
coalesce_attrs[0] = LFSR_ATTR(pos,
SHRUB(INLINED),
+lfsr_data_size(&datas[0])
+ lfsr_data_size(&datas[1]),
DATA(lfsr_data_fromcat(datas, 2)));
coalesce_attrs[1] = LFSR_ATTR(pos
+ lfsr_data_size(&datas[0])
+ lfsr_data_size(&datas[1]),
SHRUB(INLINED),
+weight
- lfsr_data_size(&datas[0])
- lfsr_data_size(&datas[1]),
DATA(datas[2]));
} else {
coalesce_attrs[0] = LFSR_ATTR(pos,
SHRUB(INLINED),
+lfsr_data_size(&datas[0]),
DATA(datas[0]));
if (lfsr_data_size(&datas[1])
+ lfsr_data_size(&datas[2])
// make sure to never write null siblings, even when
// buffer > cache size
<= lfs_max32(lfs->cfg->coalesce_size, file->buffer_size)) {
coalesce_attrs[1] = LFSR_ATTR(pos + lfsr_data_size(&datas[0]),
SHRUB(INLINED),
+weight - lfsr_data_size(&datas[0]),
DATA(lfsr_data_fromcat(datas + 1, 2)));
} else {
coalesce_attrs[1] = LFSR_ATTR(pos + lfsr_data_size(&datas[0]),
SHRUB(INLINED),
lfsr_data_size(&datas[1]),
DATA(datas[1]));
coalesce_attrs[2] = LFSR_ATTR(pos
+ lfsr_data_size(&datas[0])
+ lfsr_data_size(&datas[1]),
SHRUB(INLINED),
+weight
- lfsr_data_size(&datas[0])
- lfsr_data_size(&datas[1]),
DATA(datas[2]));
}
} }
// commit our attributes // commit our attributes
int err = lfsr_mdir_commit(lfs, &file->m.mdir, LFSR_ATTRS( int err = lfsr_mdir_commit(lfs, &file->m.mdir, LFSR_ATTRS(
LFSR_ATTR_(file->m.mdir.mid, SHRUBATTRS, 0, SHRUBATTRS(file, LFSR_ATTR_(file->m.mdir.mid, SHRUBATTRS, 0, SHRUBATTRS(file,
// adjust a hole? scratch_attrs,
hole_attr, attrs_ - scratch_attrs))));
// remove any data in the way
(lfsr_file_hasshrub(file)
? LFSR_ATTR(
lfs_min32(
pos + weight,
file->inlined.u.rbyd.weight)-1,
RM,
-(lfs_min32(
pos + weight,
file->inlined.u.rbyd.weight)
- pos),
NULL)
: LFSR_ATTR_NOOP),
// and then append our data
coalesce_attrs[0],
coalesce_attrs[1],
coalesce_attrs[2]))));
if (err) { if (err) {
return err; return err;
} }