Decomposed lfsr_bptr_t in lfsr_file_flush_ until the last minute

This breaks down the in-progress lfsr_bptr_t in lfsr_file_flush_ into
its components (block, off, eoff, cksum), until right before we write it
to disk.

This may seem counterproductive. Some paths, such as block appends,
start with an lfsr_bptr_t, but breaking it down this way avoids the
lfsr_bptr_t encoding noise and gives the compiler a better chance to
optimize things.

The end result is a nice bit of code/stack savings, and a slightly more
readable function:

           code          stack          ctx
  before: 36320           2584          640
  after:  36304 (-0.0%)   2576 (-0.3%)  640 (+0.0%)
This commit is contained in:
Christopher Haster
2025-02-04 21:58:04 -06:00
parent d248f70e6a
commit 4d02e5d646
+69 -65
View File
@@ -11544,7 +11544,11 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
lfs_off_t crystal_start = pos; lfs_off_t crystal_start = pos;
lfs_off_t crystal_end = pos + size; lfs_off_t crystal_end = pos + size;
lfs_off_t block_start; lfs_off_t block_start;
lfsr_bptr_t bptr; lfs_off_t block_end;
lfs_sblock_t block;
lfs_size_t off;
lfs_size_t eoff;
uint32_t cksum;
// within our tree? find left crystal neighbor // within our tree? find left crystal neighbor
if (pos > 0 if (pos > 0
@@ -11556,6 +11560,7 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
&& !aligned) { && !aligned) {
lfsr_bid_t bid; lfsr_bid_t bid;
lfsr_bid_t weight; lfsr_bid_t weight;
lfsr_bptr_t bptr;
int err = lfsr_file_lookupnext(lfs, file, int err = lfsr_file_lookupnext(lfs, file,
lfs_smax(pos - (lfs->cfg->crystal_thresh-1), 0), lfs_smax(pos - (lfs->cfg->crystal_thresh-1), 0),
&bid, &weight, &bptr); &bid, &weight, &bptr);
@@ -11595,6 +11600,11 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
// try to use erased-state // try to use erased-state
block_start = bid-(weight-1); block_start = bid-(weight-1);
block_end = block_start + lfsr_data_size(bptr.data);
block = bptr.data.u.disk.block;
off = bptr.data.u.disk.off;
eoff = lfsr_bptr_cksize(&bptr);
cksum = lfsr_bptr_cksum(&bptr);
goto compact; goto compact;
} }
} }
@@ -11606,6 +11616,7 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
&& file->b.shrub.weight > 0) { && file->b.shrub.weight > 0) {
lfsr_bid_t bid; lfsr_bid_t bid;
lfsr_bid_t weight; lfsr_bid_t weight;
lfsr_bptr_t bptr;
int err = lfsr_file_lookupnext(lfs, file, int err = lfsr_file_lookupnext(lfs, file,
lfs_min( lfs_min(
crystal_start + (lfs->cfg->crystal_thresh-1), crystal_start + (lfs->cfg->crystal_thresh-1),
@@ -11650,6 +11661,7 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
&& !aligned) { && !aligned) {
lfsr_bid_t bid; lfsr_bid_t bid;
lfsr_bid_t weight; lfsr_bid_t weight;
lfsr_bptr_t bptr;
int err = lfsr_file_lookupnext(lfs, file, int err = lfsr_file_lookupnext(lfs, file,
lfs_min( lfs_min(
crystal_start-1, crystal_start-1,
@@ -11679,6 +11691,11 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
file->eoff = -1; file->eoff = -1;
// try to use erased-state // try to use erased-state
block_end = block_start + lfsr_data_size(bptr.data);
block = bptr.data.u.disk.block;
off = bptr.data.u.disk.off;
eoff = lfsr_bptr_cksize(&bptr);
cksum = lfsr_bptr_cksum(&bptr);
goto compact; goto compact;
} }
@@ -11696,45 +11713,45 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
// //
// note if we relocate, we rewrite the entire block from block_start // note if we relocate, we rewrite the entire block from block_start
// using what we can find in our tree // using what we can find in our tree
lfs_sblock_t block = lfs_alloc(lfs, true); block = lfs_alloc(lfs, true);
if (block < 0) { if (block < 0) {
return block; return block;
} }
lfsr_bptr_init(&bptr, LFSR_DATA_DISK(block, 0, 0), 0, 0); block_end = block_start;
off = 0;
eoff = 0;
cksum = 0;
compact:; compact:;
// compact data into our block // compact data into our block
// //
// eagerly merge any right neighbors we see unless that would // eagerly merge any right neighbors we see unless that would
// put us over our block size // put us over our block size
lfs_off_t pos_ = block_start + lfsr_data_size(bptr.data); while (block_end < lfs_min(
while (pos_ < lfs_min(
block_start block_start
+ (lfs->cfg->block_size - bptr.data.u.disk.off), + (lfs->cfg->block_size - off),
lfs_max( lfs_max(
pos + size, pos + size,
file->b.shrub.weight))) { file->b.shrub.weight))) {
// keep track of the next highest priority data offset // keep track of the next highest priority data offset
lfs_ssize_t d = lfs_min( lfs_ssize_t d = lfs_min(
block_start block_start
+ (lfs->cfg->block_size - bptr.data.u.disk.off), + (lfs->cfg->block_size - off),
lfs_max( lfs_max(
pos + size, pos + size,
file->b.shrub.weight)) - pos_; file->b.shrub.weight)) - block_end;
// any data in our buffer? // any data in our buffer?
if (pos_ < pos + size && size > 0) { if (block_end < pos + size && size > 0) {
if (pos_ >= pos) { if (block_end >= pos) {
lfs_ssize_t d_ = lfs_min( lfs_ssize_t d_ = lfs_min(
d, d,
size - (pos_ - pos)); size - (block_end - pos));
int err = lfsr_bd_prog(lfs, bptr.data.u.disk.block, int err = lfsr_bd_prog(lfs, block,
lfsr_bptr_cksize(&bptr), eoff,
&buffer[pos_ - pos], d_, &buffer[block_end - pos], d_,
LFS_IFDEF_CKDATACKSUMS( &cksum, true);
&bptr.data.u.disk.cksum,
&bptr.cksum), true);
if (err) { if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE); LFS_ASSERT(err != LFS_ERR_RANGE);
// bad prog? try another block // bad prog? try another block
@@ -11744,23 +11761,21 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
return err; return err;
} }
pos_ += d_; block_end += d_;
LFS_IFDEF_CKDATACKSUMS( eoff += d_;
bptr.data.u.disk.cksize,
bptr.cksize) += d_;
d -= d_; d -= d_;
} }
// buffered data takes priority // buffered data takes priority
d = lfs_min(d, pos - pos_); d = lfs_min(d, pos - block_end);
} }
// any data on disk? // any data on disk?
if (pos_ < file->b.shrub.weight) { if (block_end < file->b.shrub.weight) {
lfsr_bid_t bid_; lfsr_bid_t bid_;
lfsr_bid_t weight_; lfsr_bid_t weight_;
lfsr_bptr_t bptr_; lfsr_bptr_t bptr_;
int err = lfsr_file_lookupnext(lfs, file, pos_, int err = lfsr_file_lookupnext(lfs, file, block_end,
&bid_, &weight_, &bptr_); &bid_, &weight_, &bptr_);
if (err) { if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT); LFS_ASSERT(err != LFS_ERR_NOENT);
@@ -11783,7 +11798,7 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
if (bid_-(weight_-1) >= crystal_end if (bid_-(weight_-1) >= crystal_end
// is this data a pure hole? stop early to better // is this data a pure hole? stop early to better
// leverage erased-state in sparse files // leverage erased-state in sparse files
&& (pos_ >= bid_-(weight_-1) && (block_end >= bid_-(weight_-1)
+ lfsr_data_size(bptr_.data) + lfsr_data_size(bptr_.data)
// does this data exceed our block_size? // does this data exceed our block_size?
// stop early to try to avoid messing up // stop early to try to avoid messing up
@@ -11794,21 +11809,20 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
break; break;
} }
if (pos_ < bid_-(weight_-1) + lfsr_data_size(bptr_.data)) { if (block_end
< bid_-(weight_-1) + lfsr_data_size(bptr_.data)) {
// note one important side-effect here is a strict // note one important side-effect here is a strict
// data hint // data hint
lfs_ssize_t d_ = lfs_min( lfs_ssize_t d_ = lfs_min(
d, d,
lfsr_data_size(bptr_.data) lfsr_data_size(bptr_.data)
- (pos_ - (bid_-(weight_-1)))); - (block_end - (bid_-(weight_-1))));
err = lfsr_bd_progdata(lfs, bptr.data.u.disk.block, err = lfsr_bd_progdata(lfs, block,
lfsr_bptr_cksize(&bptr), eoff,
LFSR_DATA_SLICE(bptr_.data, LFSR_DATA_SLICE(bptr_.data,
pos_ - (bid_-(weight_-1)), block_end - (bid_-(weight_-1)),
d_), d_),
LFS_IFDEF_CKDATACKSUMS( &cksum, true);
&bptr.data.u.disk.cksum,
&bptr.cksum), true);
if (err) { if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE); LFS_ASSERT(err != LFS_ERR_RANGE);
// bad prog? try another block // bad prog? try another block
@@ -11818,24 +11832,20 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
return err; return err;
} }
pos_ += d_; block_end += d_;
LFS_IFDEF_CKDATACKSUMS( eoff += d_;
bptr.data.u.disk.cksize,
bptr.cksize) += d_;
d -= d_; d -= d_;
} }
// found a hole? just make sure next leaf takes priority // found a hole? just make sure next leaf takes priority
d = lfs_min(d, bid_+1 - pos_); d = lfs_min(d, bid_+1 - block_end);
} }
// found a hole? fill with zeros // found a hole? fill with zeros
int err = lfsr_bd_set(lfs, int err = lfsr_bd_set(lfs, block,
bptr.data.u.disk.block, lfsr_bptr_cksize(&bptr), eoff,
0, d, 0, d,
LFS_IFDEF_CKDATACKSUMS( &cksum, true);
&bptr.data.u.disk.cksum,
&bptr.cksum), true);
if (err) { if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE); LFS_ASSERT(err != LFS_ERR_RANGE);
// bad prog? try another block // bad prog? try another block
@@ -11845,27 +11855,22 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
return err; return err;
} }
pos_ += d; block_end += d;
LFS_IFDEF_CKDATACKSUMS( eoff += d;
bptr.data.u.disk.cksize,
bptr.cksize) += d;
} }
// A bit of a hack here, we need to truncate our block to prog_size // A bit of a hack here, we need to truncate our block to prog_size
// alignment to avoid padding issues. Doing this retroactively to // alignment to avoid padding issues. Doing this retroactively to
// the pcache greatly simplifies the above loop, though we may end // the pcache greatly simplifies the above loop, though we may end
// up reading more than is strictly necessary. // up reading more than is strictly necessary.
lfs_ssize_t d = lfsr_bptr_cksize(&bptr) % lfs->cfg->prog_size; lfs_ssize_t d = eoff % lfs->cfg->prog_size;
lfs->pcache.size -= d; lfs->pcache.size -= d;
LFS_IFDEF_CKDATACKSUMS( block_end -= d;
bptr.data.u.disk.cksize, eoff -= d;
bptr.cksize) -= d;
// finalize our write // finalize our write
int err = lfsr_bd_flush(lfs, int err = lfsr_bd_flush(lfs,
LFS_IFDEF_CKDATACKSUMS( &cksum, true);
&bptr.data.u.disk.cksum,
&bptr.cksum), true);
if (err) { if (err) {
// bad prog? try another block // bad prog? try another block
if (err == LFS_ERR_CORRUPT) { if (err == LFS_ERR_CORRUPT) {
@@ -11875,16 +11880,15 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
} }
// prepare our block pointer // prepare our block pointer
LFS_ASSERT(lfsr_bptr_cksize(&bptr) > 0); LFS_ASSERT(eoff > 0);
LFS_ASSERT(lfsr_bptr_cksize(&bptr) <= lfs->cfg->block_size); LFS_ASSERT(eoff <= lfs->cfg->block_size);
lfsr_bptr_t bptr;
lfsr_bptr_init(&bptr, lfsr_bptr_init(&bptr,
LFSR_DATA_DISK( LFSR_DATA_DISK(
bptr.data.u.disk.block, block,
bptr.data.u.disk.off, off,
lfsr_bptr_cksize(&bptr) - bptr.data.u.disk.off), eoff - off),
lfsr_bptr_cksize(&bptr), eoff, cksum);
lfsr_bptr_cksum(&bptr));
lfs_off_t block_end = block_start + lfsr_data_size(bptr.data);
// and write it into our tree // and write it into our tree
uint8_t bptr_buf[LFSR_BPTR_DSIZE]; uint8_t bptr_buf[LFSR_BPTR_DSIZE];
@@ -11898,9 +11902,9 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file,
} }
// keep track of any remaining erased-state // keep track of any remaining erased-state
if (lfsr_bptr_cksize(&bptr) < lfs->cfg->block_size) { if (eoff < lfs->cfg->block_size) {
file->eblock = bptr.data.u.disk.block; file->eblock = block;
file->eoff = lfsr_bptr_cksize(&bptr); file->eoff = eoff;
} }
// note compacting fragments -> blocks may not actually make any // note compacting fragments -> blocks may not actually make any