Skip to content

Commit 8b1d685

Browse files
committed
Store Feature flags in line rather than on the heap by default
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.
1 parent 7fca90c commit 8b1d685

File tree

2 files changed

+183
-36
lines changed

2 files changed

+183
-36
lines changed

lightning-types/src/features.rs

Lines changed: 182 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,14 @@
9090
use core::borrow::Borrow;
9191
use core::hash::{Hash, Hasher};
9292
use core::marker::PhantomData;
93+
use core::ops::{Deref, DerefMut};
9394
use core::{cmp, fmt};
9495

9596
use alloc::vec::Vec;
9697

9798
mod sealed {
9899
use super::Features;
99100

100-
use alloc::vec::Vec;
101-
102101
/// The context in which [`Features`] are applicable. Defines which features are known to the
103102
/// implementation, though specification of them as required or optional is up to the code
104103
/// constructing a features object.
@@ -297,68 +296,68 @@ mod sealed {
297296

298297
/// Returns whether the feature is required by the given flags.
299298
#[inline]
300-
fn requires_feature(flags: &Vec<u8>) -> bool {
299+
fn requires_feature(flags: &[u8]) -> bool {
301300
flags.len() > Self::BYTE_OFFSET &&
302301
(flags[Self::BYTE_OFFSET] & Self::REQUIRED_MASK) != 0
303302
}
304303

305304
/// Returns whether the feature is supported by the given flags.
306305
#[inline]
307-
fn supports_feature(flags: &Vec<u8>) -> bool {
306+
fn supports_feature(flags: &[u8]) -> bool {
308307
flags.len() > Self::BYTE_OFFSET &&
309308
(flags[Self::BYTE_OFFSET] & (Self::REQUIRED_MASK | Self::OPTIONAL_MASK)) != 0
310309
}
311310

312311
/// Sets the feature's required (even) bit in the given flags.
313312
#[inline]
314-
fn set_required_bit(flags: &mut Vec<u8>) {
315-
if flags.len() <= Self::BYTE_OFFSET {
316-
flags.resize(Self::BYTE_OFFSET + 1, 0u8);
313+
fn set_required_bit(obj: &mut Features<Self>) {
314+
if obj.flags.len() <= Self::BYTE_OFFSET {
315+
obj.flags.resize(Self::BYTE_OFFSET + 1, 0u8);
317316
}
318317

319-
flags[Self::BYTE_OFFSET] |= Self::REQUIRED_MASK;
320-
flags[Self::BYTE_OFFSET] &= !Self::OPTIONAL_MASK;
318+
obj.flags[Self::BYTE_OFFSET] |= Self::REQUIRED_MASK;
319+
obj.flags[Self::BYTE_OFFSET] &= !Self::OPTIONAL_MASK;
321320
}
322321

323322
/// Sets the feature's optional (odd) bit in the given flags.
324323
#[inline]
325-
fn set_optional_bit(flags: &mut Vec<u8>) {
326-
if flags.len() <= Self::BYTE_OFFSET {
327-
flags.resize(Self::BYTE_OFFSET + 1, 0u8);
324+
fn set_optional_bit(obj: &mut Features<Self>) {
325+
if obj.flags.len() <= Self::BYTE_OFFSET {
326+
obj.flags.resize(Self::BYTE_OFFSET + 1, 0u8);
328327
}
329328

330-
flags[Self::BYTE_OFFSET] |= Self::OPTIONAL_MASK;
329+
obj.flags[Self::BYTE_OFFSET] |= Self::OPTIONAL_MASK;
331330
}
332331

333332
/// Clears the feature's required (even) and optional (odd) bits from the given
334333
/// flags.
335334
#[inline]
336-
fn clear_bits(flags: &mut Vec<u8>) {
337-
if flags.len() > Self::BYTE_OFFSET {
338-
flags[Self::BYTE_OFFSET] &= !Self::REQUIRED_MASK;
339-
flags[Self::BYTE_OFFSET] &= !Self::OPTIONAL_MASK;
335+
fn clear_bits(obj: &mut Features<Self>) {
336+
if obj.flags.len() > Self::BYTE_OFFSET {
337+
obj.flags[Self::BYTE_OFFSET] &= !Self::REQUIRED_MASK;
338+
obj.flags[Self::BYTE_OFFSET] &= !Self::OPTIONAL_MASK;
340339
}
341340

342-
let last_non_zero_byte = flags.iter().rposition(|&byte| byte != 0);
341+
let last_non_zero_byte = obj.flags.iter().rposition(|&byte| byte != 0);
343342
let size = if let Some(offset) = last_non_zero_byte { offset + 1 } else { 0 };
344-
flags.resize(size, 0u8);
343+
obj.flags.resize(size, 0u8);
345344
}
346345
}
347346

348347
impl <T: $feature> Features<T> {
349348
/// Set this feature as optional.
350349
pub fn $optional_setter(&mut self) {
351-
<T as $feature>::set_optional_bit(&mut self.flags);
350+
<T as $feature>::set_optional_bit(self);
352351
}
353352

354353
/// Set this feature as required.
355354
pub fn $required_setter(&mut self) {
356-
<T as $feature>::set_required_bit(&mut self.flags);
355+
<T as $feature>::set_required_bit(self);
357356
}
358357

359358
/// Unsets this feature.
360359
pub fn $clear(&mut self) {
361-
<T as $feature>::clear_bits(&mut self.flags);
360+
<T as $feature>::clear_bits(self);
362361
}
363362

364363
/// Checks if this feature is supported.
@@ -661,6 +660,9 @@ mod sealed {
661660
supports_trampoline_routing,
662661
requires_trampoline_routing
663662
);
663+
// By default, allocate enough bytes to cover up to Trampoline. Update this as new features are
664+
// added which we expect to appear commonly across contexts.
665+
pub(super) const MIN_FEATURES_ALLOCATION_BYTES: usize = (57 + 7) / 8;
664666
define_feature!(
665667
259,
666668
DnsResolver,
@@ -700,14 +702,138 @@ mod sealed {
700702
const ANY_REQUIRED_FEATURES_MASK: u8 = 0b01_01_01_01;
701703
const ANY_OPTIONAL_FEATURES_MASK: u8 = 0b10_10_10_10;
702704

705+
// Vecs are always 3 pointers long, so `FeatureFlags` is never shorter than 24 bytes on 64-bit
706+
// platforms no matter what we do.
707+
//
708+
// Luckily, because `Vec` uses a `NonNull` pointer to its buffer, the two-variant enum is free
709+
// space-wise, but we only get the remaining 2 usizes in length available for our own stuff (as any
710+
// other value is interpreted as the `Heap` variant).
711+
//
712+
// Thus, as long as we never use more than 16 bytes (15 bytes for the data and one byte for the
713+
// length) for our Held variant `FeatureFlags` is the same length as a `Vec` in memory.
714+
const DIRECT_ALLOC_BYTES: usize = if sealed::MIN_FEATURES_ALLOCATION_BYTES > 8 * 2 - 1 {
715+
sealed::MIN_FEATURES_ALLOCATION_BYTES
716+
} else {
717+
8 * 2 - 1
718+
};
719+
const _ASSERT: () = assert!(DIRECT_ALLOC_BYTES <= u8::MAX as usize);
720+
721+
#[cfg(fuzzing)]
722+
#[derive(Clone, PartialEq, Eq)]
723+
pub enum FeatureFlags {
724+
Held { bytes: [u8; DIRECT_ALLOC_BYTES], len: u8 },
725+
Heap(Vec<u8>),
726+
}
727+
728+
#[cfg(not(fuzzing))]
729+
#[derive(Clone, PartialEq, Eq)]
730+
enum FeatureFlags {
731+
Held { bytes: [u8; DIRECT_ALLOC_BYTES], len: u8 },
732+
Heap(Vec<u8>),
733+
}
734+
735+
impl FeatureFlags {
736+
fn empty() -> Self {
737+
Self::Held { bytes: [0; DIRECT_ALLOC_BYTES], len: 0 }
738+
}
739+
740+
fn from(vec: Vec<u8>) -> Self {
741+
if vec.len() <= DIRECT_ALLOC_BYTES {
742+
let mut bytes = [0; DIRECT_ALLOC_BYTES];
743+
bytes[..vec.len()].copy_from_slice(&vec);
744+
Self::Held { bytes, len: vec.len() as u8 }
745+
} else {
746+
Self::Heap(vec)
747+
}
748+
}
749+
750+
fn resize(&mut self, new_len: usize, default: u8) {
751+
match self {
752+
Self::Held { bytes, len } => {
753+
let start_len = *len as usize;
754+
if new_len <= DIRECT_ALLOC_BYTES {
755+
bytes[start_len..].copy_from_slice(&[default; DIRECT_ALLOC_BYTES][start_len..]);
756+
*len = new_len as u8;
757+
} else {
758+
let mut vec = Vec::new();
759+
vec.resize(new_len, default);
760+
vec[..start_len].copy_from_slice(&bytes[..start_len]);
761+
*self = Self::Heap(vec);
762+
}
763+
},
764+
Self::Heap(vec) => {
765+
vec.resize(new_len, default);
766+
if new_len <= DIRECT_ALLOC_BYTES {
767+
let mut bytes = [0; DIRECT_ALLOC_BYTES];
768+
bytes[..new_len].copy_from_slice(&vec[..new_len]);
769+
*self = Self::Held { bytes, len: new_len as u8 };
770+
}
771+
},
772+
}
773+
}
774+
775+
fn len(&self) -> usize {
776+
self.deref().len()
777+
}
778+
779+
fn iter(
780+
&self,
781+
) -> (impl Clone + ExactSizeIterator<Item = &u8> + DoubleEndedIterator<Item = &u8>) {
782+
let slice = self.deref();
783+
slice.iter()
784+
}
785+
786+
fn iter_mut(
787+
&mut self,
788+
) -> (impl ExactSizeIterator<Item = &mut u8> + DoubleEndedIterator<Item = &mut u8>) {
789+
let slice = self.deref_mut();
790+
slice.iter_mut()
791+
}
792+
}
793+
794+
impl Deref for FeatureFlags {
795+
type Target = [u8];
796+
fn deref(&self) -> &[u8] {
797+
match self {
798+
FeatureFlags::Held { bytes, len } => &bytes[..*len as usize],
799+
FeatureFlags::Heap(vec) => &vec,
800+
}
801+
}
802+
}
803+
804+
impl DerefMut for FeatureFlags {
805+
fn deref_mut(&mut self) -> &mut [u8] {
806+
match self {
807+
FeatureFlags::Held { bytes, len } => &mut bytes[..*len as usize],
808+
FeatureFlags::Heap(vec) => &mut vec[..],
809+
}
810+
}
811+
}
812+
813+
impl PartialOrd for FeatureFlags {
814+
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
815+
Some(self.cmp(other))
816+
}
817+
}
818+
impl Ord for FeatureFlags {
819+
fn cmp(&self, other: &Self) -> cmp::Ordering {
820+
self.deref().cmp(other.deref())
821+
}
822+
}
823+
impl fmt::Debug for FeatureFlags {
824+
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
825+
self.deref().fmt(fmt)
826+
}
827+
}
828+
703829
/// Tracks the set of features which a node implements, templated by the context in which it
704830
/// appears.
705831
///
706832
/// This is not exported to bindings users as we map the concrete feature types below directly instead
707833
#[derive(Eq)]
708-
pub struct Features<T: sealed::Context> {
834+
pub struct Features<T: sealed::Context + ?Sized> {
709835
/// Note that, for convenience, flags is LITTLE endian (despite being big-endian on the wire)
710-
flags: Vec<u8>,
836+
flags: FeatureFlags,
711837
mark: PhantomData<T>,
712838
}
713839

@@ -744,7 +870,7 @@ impl<T: sealed::Context> Hash for Features<T> {
744870
nonzero_flags.hash(hasher);
745871
}
746872
}
747-
impl<T: sealed::Context> PartialEq for Features<T> {
873+
impl<T: sealed::Context + ?Sized> PartialEq for Features<T> {
748874
fn eq(&self, o: &Self) -> bool {
749875
let mut o_iter = o.flags.iter();
750876
let mut self_iter = self.flags.iter();
@@ -879,25 +1005,23 @@ impl ChannelTypeFeatures {
8791005
/// Constructs a ChannelTypeFeatures with only static_remotekey set
8801006
pub fn only_static_remote_key() -> Self {
8811007
let mut ret = Self::empty();
882-
<sealed::ChannelTypeContext as sealed::StaticRemoteKey>::set_required_bit(&mut ret.flags);
1008+
<sealed::ChannelTypeContext as sealed::StaticRemoteKey>::set_required_bit(&mut ret);
8831009
ret
8841010
}
8851011

8861012
/// Constructs a ChannelTypeFeatures with anchors support
8871013
pub fn anchors_zero_htlc_fee_and_dependencies() -> Self {
8881014
let mut ret = Self::empty();
889-
<sealed::ChannelTypeContext as sealed::StaticRemoteKey>::set_required_bit(&mut ret.flags);
890-
<sealed::ChannelTypeContext as sealed::AnchorsZeroFeeHtlcTx>::set_required_bit(
891-
&mut ret.flags,
892-
);
1015+
<sealed::ChannelTypeContext as sealed::StaticRemoteKey>::set_required_bit(&mut ret);
1016+
<sealed::ChannelTypeContext as sealed::AnchorsZeroFeeHtlcTx>::set_required_bit(&mut ret);
8931017
ret
8941018
}
8951019
}
8961020

8971021
impl<T: sealed::Context> Features<T> {
8981022
/// Create a blank Features with no features set
8991023
pub fn empty() -> Self {
900-
Features { flags: Vec::new(), mark: PhantomData }
1024+
Features { flags: FeatureFlags::empty(), mark: PhantomData }
9011025
}
9021026

9031027
/// Converts `Features<T>` to `Features<C>`. Only known `T` features relevant to context `C` are
@@ -910,7 +1034,7 @@ impl<T: sealed::Context> Features<T> {
9101034
None
9111035
}
9121036
});
913-
let mut flags = Vec::new();
1037+
let mut flags = FeatureFlags::empty();
9141038
flags.resize(flag_iter.clone().count(), 0);
9151039
for (i, byte) in flag_iter {
9161040
flags[i] = byte;
@@ -923,7 +1047,7 @@ impl<T: sealed::Context> Features<T> {
9231047
///
9241048
/// This is not exported to bindings users as we don't support export across multiple T
9251049
pub fn from_le_bytes(flags: Vec<u8>) -> Features<T> {
926-
Features { flags, mark: PhantomData }
1050+
Features { flags: FeatureFlags::from(flags), mark: PhantomData }
9271051
}
9281052

9291053
/// Returns the feature set as a list of bytes, in little-endian. This is in reverse byte order
@@ -938,7 +1062,7 @@ impl<T: sealed::Context> Features<T> {
9381062
/// This is not exported to bindings users as we don't support export across multiple T
9391063
pub fn from_be_bytes(mut flags: Vec<u8>) -> Features<T> {
9401064
flags.reverse(); // Swap to little-endian
941-
Self { flags, mark: PhantomData }
1065+
Self { flags: FeatureFlags::from(flags), mark: PhantomData }
9421066
}
9431067

9441068
/// Returns true if this `Features` has any optional flags set
@@ -1331,7 +1455,7 @@ mod tests {
13311455
use std::hash::{Hash, Hasher};
13321456

13331457
let mut zerod_features = InitFeatures::empty();
1334-
zerod_features.flags = vec![0];
1458+
zerod_features.flags = FeatureFlags::Heap(vec![0]);
13351459
let empty_features = InitFeatures::empty();
13361460
assert!(empty_features.flags.is_empty());
13371461

@@ -1343,4 +1467,27 @@ mod tests {
13431467
empty_features.hash(&mut empty_hash);
13441468
assert_eq!(zerod_hash.finish(), empty_hash.finish());
13451469
}
1470+
1471+
#[test]
1472+
fn test_feature_flags_transitions() {
1473+
// Tests transitions from stack to heap and back in `FeatureFlags`
1474+
let mut flags = FeatureFlags::empty();
1475+
assert!(matches!(flags, FeatureFlags::Held { .. }));
1476+
1477+
flags.resize(DIRECT_ALLOC_BYTES, 42);
1478+
assert_eq!(flags.len(), DIRECT_ALLOC_BYTES);
1479+
assert!(flags.iter().take(DIRECT_ALLOC_BYTES).all(|b| *b == 42));
1480+
assert!(matches!(flags, FeatureFlags::Held { .. }));
1481+
1482+
flags.resize(DIRECT_ALLOC_BYTES * 2, 43);
1483+
assert_eq!(flags.len(), DIRECT_ALLOC_BYTES * 2);
1484+
assert!(flags.iter().take(DIRECT_ALLOC_BYTES).all(|b| *b == 42));
1485+
assert!(flags.iter().skip(DIRECT_ALLOC_BYTES).all(|b| *b == 43));
1486+
assert!(matches!(flags, FeatureFlags::Heap(_)));
1487+
1488+
flags.resize(DIRECT_ALLOC_BYTES, 0);
1489+
assert_eq!(flags.len(), DIRECT_ALLOC_BYTES);
1490+
assert!(flags.iter().take(DIRECT_ALLOC_BYTES).all(|b| *b == 42));
1491+
assert!(matches!(flags, FeatureFlags::Held { .. }));
1492+
}
13461493
}

lightning-types/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
//! See the `lightning` crate for usage of these.
1515
1616
#![cfg_attr(not(test), no_std)]
17-
#![deny(missing_docs)]
17+
#![cfg_attr(not(fuzzing), deny(missing_docs))]
1818
#![forbid(unsafe_code)]
1919
#![deny(rustdoc::broken_intra_doc_links)]
2020
#![deny(rustdoc::private_intra_doc_links)]

0 commit comments

Comments
 (0)