mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-14 10:22:38 +00:00
Use SmallVec in Bitfield (#3025)
## Issue Addressed Alternative to #2935 ## Proposed Changes Replace the `Vec<u8>` inside `Bitfield` with a `SmallVec<[u8; 32>`. This eliminates heap allocations for attestation bitfields until we reach 500K validators, at which point we can consider increasing `SMALLVEC_LEN` to 40 or 48. While running Lighthouse under `heaptrack` I found that SSZ encoding and decoding of bitfields corresponded to 22% of all allocations by count. I've confirmed that with this change applied those allocations disappear entirely. ## Additional Info We can win another 8 bytes of space by using `smallvec`'s [`union` feature](https://docs.rs/smallvec/1.8.0/smallvec/#union), although I might leave that for a future PR because I don't know how experimental that feature is and whether it uses some spicy `unsafe` blocks.
This commit is contained in:
@@ -45,6 +45,7 @@ parking_lot = "0.11.1"
|
||||
itertools = "0.10.0"
|
||||
superstruct = "0.4.0"
|
||||
serde_json = "1.0.74"
|
||||
smallvec = "1.8.0"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.3.3"
|
||||
|
||||
@@ -110,9 +110,34 @@ impl<T: EthSpec> SlotData for Attestation<T> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::*;
|
||||
|
||||
use super::*;
|
||||
// Check the in-memory size of an `Attestation`, which is useful for reasoning about memory
|
||||
// and preventing regressions.
|
||||
//
|
||||
// This test will only pass with `blst`, if we run these tests with Milagro or another
|
||||
// BLS library in future we will have to make it generic.
|
||||
#[test]
|
||||
fn size_of() {
|
||||
use std::mem::size_of;
|
||||
|
||||
let aggregation_bits =
|
||||
size_of::<BitList<<MainnetEthSpec as EthSpec>::MaxValidatorsPerCommittee>>();
|
||||
let attestation_data = size_of::<AttestationData>();
|
||||
let signature = size_of::<AggregateSignature>();
|
||||
|
||||
assert_eq!(aggregation_bits, 56);
|
||||
assert_eq!(attestation_data, 128);
|
||||
assert_eq!(signature, 288 + 16);
|
||||
|
||||
let attestation_expected = aggregation_bits + attestation_data + signature;
|
||||
assert_eq!(attestation_expected, 488);
|
||||
assert_eq!(
|
||||
size_of::<Attestation<MainnetEthSpec>>(),
|
||||
attestation_expected
|
||||
);
|
||||
}
|
||||
|
||||
ssz_and_tree_hash_tests!(Attestation<MainnetEthSpec>);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use super::*;
|
||||
use crate::{BitList, BitVector, Unsigned};
|
||||
use smallvec::smallvec;
|
||||
|
||||
impl<N: Unsigned + Clone> TestRandom for BitList<N> {
|
||||
fn random_for_test(rng: &mut impl RngCore) -> Self {
|
||||
let mut raw_bytes = vec![0; std::cmp::max(1, (N::to_usize() + 7) / 8)];
|
||||
let mut raw_bytes = smallvec![0; std::cmp::max(1, (N::to_usize() + 7) / 8)];
|
||||
rng.fill_bytes(&mut raw_bytes);
|
||||
Self::from_bytes(raw_bytes).expect("we generate a valid BitList")
|
||||
}
|
||||
@@ -11,7 +12,7 @@ impl<N: Unsigned + Clone> TestRandom for BitList<N> {
|
||||
|
||||
impl<N: Unsigned + Clone> TestRandom for BitVector<N> {
|
||||
fn random_for_test(rng: &mut impl RngCore) -> Self {
|
||||
let mut raw_bytes = vec![0; std::cmp::max(1, (N::to_usize() + 7) / 8)];
|
||||
let mut raw_bytes = smallvec![0; std::cmp::max(1, (N::to_usize() + 7) / 8)];
|
||||
rng.fill_bytes(&mut raw_bytes);
|
||||
Self::from_bytes(raw_bytes).expect("we generate a valid BitVector")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user