Skip to content

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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

TheBlueMatt
Copy link
Collaborator

In a running LDK instance we generally have a ton of Features 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.

@ldk-reviews-bot
Copy link

ldk-reviews-bot commented Apr 10, 2025

I've assigned @valentinewallace as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@TheBlueMatt TheBlueMatt force-pushed the 2025-04-features-less-alloc branch 4 times, most recently from 61824a3 to 03aaa63 Compare April 10, 2025 23:54
This avoids vec doubling while doing `Features` conversions.
@TheBlueMatt TheBlueMatt force-pushed the 2025-04-features-less-alloc branch from 03aaa63 to 1160c31 Compare April 11, 2025 00:26
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.
#[derive(Clone, PartialEq, Eq)]
#[doc(hidden)]
pub enum FeatureFlags {
Held { bytes: [u8; DIRECT_ALLOC_BYTES], len: u8 },
Copy link
Contributor

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?

Copy link
Collaborator Author

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.

Copy link
Contributor

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.

Copy link
Collaborator Author

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.

@ldk-reviews-bot
Copy link

🔔 1st Reminder

Hey @valentinewallace! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

Comment on lines 712 to 713
// 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.
Copy link
Contributor

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 usizes as mentioned above) because we need a byte to write the type of features, or? Would be helpful to clarify that.

Copy link
Collaborator Author

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.

No new datastructure would be complete without a dedicated fuzzer,
so we add one here.
@TheBlueMatt TheBlueMatt force-pushed the 2025-04-features-less-alloc branch from 2dd6c18 to fadb54f Compare April 15, 2025 13:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants