Replaced inlined lfsr_data_t with a lazily encoded leb128

The idea is that we can save on the cost of calling lfs_toleb128
everywhere we commit leb128s, by lazily encoding during progdata.

I original thought this would have too many small problems, but:

1. We can actually implement slice surprisingly easily by just shifting
   the internal word 7 bits. This emulates byte-level slicing in the
   encoded leb128.

   This enables read/cmp, so we can implement all of the lfsr_data_t
   functions, though it does make lfs_toleb128 required for a readonly
   implementation, which isn't great. Sufficient creativity with ifdefs
   likely makes this a non-problem though.

2. There's really very limited use cases for non-leb128 inlined datas.

   We can use it to encode the version and compatflags during
   lfs_format, but that's about it. And lfs_format is definitely not on
   the stack hot-path, so there's no reason to not use on-stack buffers
   for these.

The original motivation for this change was noticing a surprising amount
of code savings related to lazy leb128 encoding in another lfsr_data_t
refactor. Unfortunately this savings does not seem reproducible:

           code          stack
  before: 33864           2880
  after:  33912 (+0.1%)   2888 (+0.3%)

But that's ok, this is closer to what I expected. The lfs_sizeleb128
call we need to predict the leb128 size is close to the same cost as
calling lfs_toleb128 so the savings isn't really that much.
This commit is contained in:
Christopher Haster
2024-02-25 12:08:26 -06:00
parent 5005db2b4e
commit 415e148f62
4 changed files with 86 additions and 97 deletions
+7 -13
View File
@@ -399,17 +399,11 @@ typedef struct lfs_mdir {
// Either an on-disk or in-device data pointer
//
// The sign-bit of the size field indicates if the data is
// in-device or on-disk.
//
// After removing the sign bit, the size always encodes the
// resulting size on-disk.
//
// The exact representation of in-device data also depends on the
// mode field:
// - pointer to a RAM-backed buffer
// - inlined data able to fit at least 1 leb128
// - an array of concatenated datas
// The top 2 bits of data's size indicates the actual encoding
// 0b00 => in-RAM buffer
// 0b01 => a single leb128
// 0b10 => on-disk reference
// 0b11 => concatenated datas
//
// Note concatenated datas can only be 1 level deep. Concatenating
// concatenated datas would require recursion to resolve.
@@ -428,8 +422,8 @@ typedef struct lfsr_data {
} buf;
struct {
lfs_ssize_t size;
uint8_t buf[8];
} imm;
uint32_t word;
} word;
struct {
lfs_ssize_t size;
const struct lfsr_data *datas;