Attempted to re-add inlined lfsr_data_t representation

The idea, which has floated up a few times, is to add a third
representation of lfsr_data_t where the data is inlined in the struct
directly. In theory saving RAM for small pieces of data such as dids,
leb128s, flags, etc:

  inlined:             in-RAM buffer:       on-disk:
  .---+---+---+---.    .---+---+---+---.    .---+---+---+---.
  |01|   size     |    |00|   size     |    |1|    size     |
  +---+---+---+---+    +---+---+---+---+    +---+---+---+---+
  | inlined data  |    |      ptr -------.  |     block     |
  +               +    +---+---+---+---+ |  +---+---+---+---+
  |               |    |    (unused)   | |  |      off      |
  '---+---+---+---'    '---+---+---+---' |  '---+---+---+---'
                       .---+---+---+---. |
                       |     data      |<'
                       :       :       :

Unfortunately in practice this just doesn't work out.

It turns out we benefit a lot from the _simplicity_ of lfsr_data_t. When
lfsr_data_t is built out of simple words, the compiler can make some
pretty strong assumptions and basically break it down into simple
register operations.

When you stick a byte array in the middle of the struct, this sort of
breaks down.

---

We can see this in our code measurements. After adding inlined data, but
before implementing slicing (in lfsr_data_fromslice), we can see decent
stack savings. But as soon as we add the memmove to lfsr_data_fromslice,
any benefit is lost:

                        code          stack          ctx
  before:              38060           2608          752
  without slicing:     38056 (-0.0%)   2568 (-1.5%)  752 (+0.0%)
  after:               38128 (+0.2%)   2672 (+2.5%)  752 (+0.0%)

One reason for this is the extra logic does cause lfsr_data_fromslice to
be no longer inlined, but adding __attribute__((always_inline)) only
claws back some of the code/stack savings (though it's interesting to
note the compiler heuristic failure here):

                        code          stack          ctx
  before:              38060           2608          752
  after+inline:        38128 (+0.2%)   2672 (+2.5%)  752 (+0.0%)
  after+always_inline: 38684 (+1.6%)   2656 (+1.8%)  752 (+0.0%)

---

Oh, and inlined lfsr_data_t is no longer compatible with LFSR_RAT's
simple data conversion, since lfsr_rat_t's can only point to existing
buffers. This causes tests to fail rather quickly.

This should be reverted, but I think the hidden cost of inlined
lfsr_data_t is surprising and interesting to note.
This commit is contained in:
Christopher Haster
2025-01-05 14:58:49 -06:00
parent 80f4e0b825
commit 0b9f46e7cb
2 changed files with 109 additions and 56 deletions
+5 -2
View File
@@ -614,11 +614,14 @@ typedef struct lfsr_omdir {
// either an on-disk or in-device data pointer
typedef struct lfsr_data {
// sign(size)=0 => in-RAM buffer
// sign(size)=1 => on-disk reference
// the top bits of size indicate the exact encoding:
// top2(size)=0b00 => in-RAM buffer
// top2(size)=0b01 => inlined data
// top2(size)=0b1x => on-disk reference
lfs_size_t size;
union {
const uint8_t *buffer;
uint8_t imm[8];
struct {
lfs_block_t block;
lfs_size_t off;