Implemented deferred btree inlining via cutoff parameter
This finally provides a solution for deferred B-tree inlining without needing to evaluate attrs. Deferred inlining is the idea that instead of inlining B-trees as soon as the number of entries drops to either 1 or 0, we wait until a compaction occurs to inline a B-tree. This accomplishes a few things: 1. Limits any extra reads for conditions to compaction time. 2. Avoids wasting erased bytes if we drop to 1 or 0 entries only temporarily. 3. Avoids excessive erase costs if we oscillate between ~1 and ~2 entries. Unfortunately after moving away from evaluating attrs, deferred inlining became deceptively tricky. In the current, non-evaluating-attr implementation, our btree commits always lag one commit behind. When we compact, we first compact everything currently in the rbyd, and then append any pending attr. Never needing to evaluate the attrs removes a big chunk of logic as long as we can assert that the largest attr set fits after compaction. But this lagging of commits presents a problem for deferred inlining, if we detect an inlinable tree during compaction, we can't be sure it's _actually_ inlinable until we evaluate our attr. Which we really don't want to do. The solution here is to move the problem up a level. Instead of trying to determine when to inline purely from the provided attr, we require higher-level functions to provide this info in the form of a "cutoff". Where, if compaction results in fewer entries than this cutoff, the higher-level function can instead inline. This effectively allows the higher-level functions to intercept unnecessary compactions that can be inlined. So far this solution seems to work quite well, with the added plus of consolidating the corner cases around inlined/inlining btrees in these higher-level functions. --- Note that this has the peculiar side-effect of allowing zero-weight, non-inlined B-trees. Our previous internal B-tree struct using the sign of an integer to determine inline-ness, this was changed to use just the sign-bit for the condition as a sort of ones-complement width field. I think this sort of encoding may actually bit a tiny bit more efficient. I was poking around with thumb code and noticed there is no actual "abs" instruction, with gcc outputing an "it" sequence. But there is a cheap bit-clear "bic" instruction.
This commit is contained in:
@@ -360,21 +360,23 @@ typedef struct lfsr_rbyd {
|
||||
//
|
||||
// Pointers we store:
|
||||
// - block addresses => 1 leb128 => 5 bytes (worst case)
|
||||
#define LFSR_BTREE_INLINE_SIZE 5
|
||||
#define LFSR_BTREE_INLINESIZE 5
|
||||
|
||||
typedef union lfsr_btree {
|
||||
// note this lines up with weight in lfsr_rbyd_t
|
||||
//
|
||||
// weight=0 => null btree
|
||||
// weight<0 => inlined btree
|
||||
// weight>0 => normal btree
|
||||
lfs_ssize_t weight;
|
||||
// sign(weight)=1 => inlined btree
|
||||
// sign(weight)=0 => normal btree
|
||||
//
|
||||
// note due to defered inlining, both normal and inlined
|
||||
// btrees can have a weight of 0
|
||||
lfs_size_t weight;
|
||||
lfsr_rbyd_t root;
|
||||
struct {
|
||||
lfs_ssize_t weight;
|
||||
lfsr_tag_t tag;
|
||||
uint16_t size;
|
||||
uint8_t buffer[LFSR_BTREE_INLINE_SIZE];
|
||||
uint8_t buffer[LFSR_BTREE_INLINESIZE];
|
||||
} inlined;
|
||||
} lfsr_btree_t;
|
||||
|
||||
@@ -391,7 +393,7 @@ typedef union lfsr_btree {
|
||||
// union {
|
||||
// struct {
|
||||
// uint8_t size;
|
||||
// uint8_t buffer[LFSR_BTREE_INLINE_SIZE];
|
||||
// uint8_t buffer[LFSR_BTREE_INLINESIZE];
|
||||
// } inlined;
|
||||
//
|
||||
// // if we're not inlined, point to the trunk rbyd block of the btree
|
||||
|
||||
Reference in New Issue
Block a user