-
Notifications
You must be signed in to change notification settings - Fork 403
Store Feature
flags in line rather than on the heap by default (without increasing their size)
#3730
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Store Feature
flags in line rather than on the heap by default (without increasing their size)
#3730
Conversation
👋 Thanks for assigning @tnull as a reviewer! |
61824a3
to
03aaa63
Compare
This avoids vec doubling while doing `Features` conversions.
1160c31
to
2dd6c18
Compare
#[derive(Clone, PartialEq, Eq)] | ||
#[doc(hidden)] | ||
pub enum FeatureFlags { | ||
Held { bytes: [u8; DIRECT_ALLOC_BYTES], len: u8 }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should these be called OnStack
/OnHeap
or similar?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But its not necessarily on stack - its just in the object, and the object may be on heap or on stack.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mhh. Good point, I just found the current naming a bit odd.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe Heap
and Direct
? I agree its odd but I don't know what's better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe Heap and Direct?
Yeah, maybe? I guess if we're not conclusive about what's better, could also leave as is.
🔔 1st Reminder Hey @valentinewallace! This PR has been waiting for your review. |
lightning-types/src/features.rs
Outdated
// Thus, as long as we never use more than 15 bytes for our Held variant `FeatureFlags` is the same | ||
// length as a `Vec` in memory. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it not 16 bytes (2 usize
s as mentioned above) because we need a byte to write the type of features, or? Would be helpful to clarify that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, 16 bytes minus the length byte, I'll clarify.
2dd6c18
to
fadb54f
Compare
Seems CI is stuck here? |
fadb54f
to
a07c020
Compare
Went ahead and squashed to see if CI will run again. |
In a running LDK instance we generally have a ton of `Feature`s objects flying around, including ones per channel/peer in the `NetworkGraph`, ones per channel/peer in `Channel`/`ChannelManager`, and ones inside of BOLT 11/12 Invoices. Thus, its useful to avoid unecessary allocations in them to reduce heap fragmentation. Luckily, Features generally don't have more than 15 bytes worth of flags, and because `Vec` has a `NonNull` pointer we can actually wrap a vec in a two-variant enum with zero additional space penalty. While this patch leaves actually deserializing `Features` without allocating as a future exercise, immediately releasing the allocation is much better than holding it, and `Features` constructed through repeated `set_*` calls benefit from avoiding the Vec entirely.
a07c020
to
4a841aa
Compare
Also made clippy happy: $ git diff-tree -U2 a07c020d4 4a841aac3diff --git a/lightning-types/src/features.rs b/lightning-types/src/features.rs
index 804083d65..d97347b47 100644
--- a/lightning-types/src/features.rs
+++ b/lightning-types/src/features.rs
@@ -816,5 +816,5 @@ impl DerefMut for FeatureFlags {
impl PartialOrd for FeatureFlags {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
- self.deref().partial_cmp(other.deref())
+ Some(self.cmp(other))
}
} |
lightning-types/src/features.rs
Outdated
} | ||
} | ||
|
||
fn resize(&mut self, new_len: usize, default: u8) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like we'd always want to resize with 0
in this context? Not sure this parameter is useful
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was just to keep the API identical to Vec
's. I can drop it, but its also not adding complexity I think.
assert_eq!(v.len(), feat.len()); | ||
assert_eq!(v.deref(), feat.deref()); | ||
assert_eq!(old_v.deref_mut(), old_feat.deref_mut()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not have the same checks for the new and old sets of features?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We call this method once for each updated set of features. The old features here were already passed to this method as the new features in the previous loop iteration.
if data.len() % 3 != 0 { | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess you could just redefine data
to be a slice terminating 1 or 2 items early in the original slice, not a big deal though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, the fuzzer can deal :)
No new datastructure would be complete without a dedicated fuzzer, so we add one here.
4a841aa
to
f491278
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3730 +/- ##
==========================================
+ Coverage 89.11% 90.99% +1.87%
==========================================
Files 156 158 +2
Lines 123514 138442 +14928
Branches 123514 138442 +14928
==========================================
+ Hits 110070 125970 +15900
+ Misses 10762 9853 -909
+ Partials 2682 2619 -63 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
In a running LDK instance we generally have a ton of
Feature
s objects flying around, including ones per channel/peer in theNetworkGraph
, ones per channel/peer inChannel
/ChannelManager
, and ones inside of BOLT 11/12 Invoices.Thus, its useful to avoid unecessary allocations in them to reduce heap fragmentation.
Luckily, Features generally don't have more than 15 bytes worth of flags, and because
Vec
has aNonNull
pointer we can actually wrap a vec in a two-variant enum with zero additional space penalty.While this patch leaves actually deserializing
Features
without allocating as a future exercise, immediately releasing the allocation is much better than holding it, andFeatures
constructed through repeatedset_*
calls benefit from avoiding the Vec entirely.