Adopted rid-bound-dependent compressed mids.

This adopts a previously discarded idea for compressed mids with a few
tweaks to avoiding decoding the bid/rid portions as much as possible.

The idea of compressed mids is to shove both the mid bid and mid rid
into a single integer, saving RAM and potentially helping filesystem
integration where a unique per-file integer is useful.

Unfortunately this has proven tricky. littlefs fundamentally needs two
ids, one "bid" to lookup which mdir our entry resides on, and one "rid"
to lookup the entry in the mdir. It's tempting to use two half-sized
integers (16-bit for example), but this risks surprising limitations
around the number of files when blocks are either really large or
really small.

Optimally, we'd limit the number of bits reserved for the rid to the
upper bound of number of rids that can fit in a single mdir. This would
allows for more bids when the block size is small, and more rids when
the block size is large. This should roughly approximate the limits of
a per-file integer.

With a bit of math we can estimate the upper bound to be <=block_size/16
with our current compaction strategy.

This idea was previously discarded due to the overhead of extracting the
bids/rids when we need them, but the RAM savings and file-to-integer
mapping is too useful to give up. When it became clear half-width
integers wasn't really going to work, compressed mids became the new
plan:

  0bbbbbbb bbbbbbbb bbbbbbbb rrrrrrrr
  ^'-----------+-----------' '---+--'
  '------------|-----------------|---- sign-bit, reserved for driver
               |                 '---- nlog2(bs/16) bits for rid
               |                       (8-bits for 4KiB blocks)
               '---------------------- remaining bits for bid
                                       (23-bits for 4KiB blocks)

To reduce the overhead of encoding/decode bids/rids a few extra features
were added to the internal mdir APIs:

1. The mtree has been changed to store mids directly. Giving each mdir
   the upper bound as a weight. This allows direct lookup of mids
   without any sort of bid decoding, though does bake the upper bound
   estimate into the metadata of the filesystem, which isn't the
   cleanest design, but if it works it works.

   On the plus side, with this upper bound baked in to the filesystems,
   GRMs can be encoded in a single leb128, which is nice. This may have
   other savings if we ever store mids anywhere else in the filesystem.

2. rids are now mid relative in lfsr_mdir_lookup when non-negative. This
   is implemented with a simple condition that is hopefully optimized
   out when inlined, though there may be some room for improvement here.

3. rids are now mid relative in lfsr_mdir_commit. This was a bit tricky,
   but we can leverage the existing mechanisms for bid-relative rids
   used in the btree implementation.

The above changes make it so you can pass the mid around directly for
most of the mdir functions, hopefully reducing the mid decoding
overhead. This savings should only grow as more high-level filesystem
APIs are added.

Here is the resulting code/RAM changes for this entire change (from
before we adopted the mroot bit):

            code          stack          structs
  before:  20590           1784              908
  after:   20890 (+1.4%)   1744 (-2.3%)      864 (-5.1%)
This commit is contained in:
Christopher Haster
2023-08-30 14:56:54 -05:00
parent 2ea569c746
commit a9b81820b0
4 changed files with 640 additions and 650 deletions
+428 -425
View File
File diff suppressed because it is too large Load Diff