Use modular_bitfield to bitpack IndexEntry (#22447)

This commit is contained in:
Brooks Prumo
2022-01-12 14:37:34 -06:00
committed by GitHub
parent 7171c95bdd
commit eaae2f3538
4 changed files with 85 additions and 20 deletions

22
Cargo.lock generated
View File

@ -2625,6 +2625,27 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "modular-bitfield"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74"
dependencies = [
"modular-bitfield-impl",
"static_assertions",
]
[[package]]
name = "modular-bitfield-impl"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789"
dependencies = [
"proc-macro2 1.0.32",
"quote 1.0.10",
"syn 1.0.81",
]
[[package]] [[package]]
name = "multimap" name = "multimap"
version = "0.8.3" version = "0.8.3"
@ -4585,6 +4606,7 @@ dependencies = [
"fs_extra", "fs_extra",
"log 0.4.14", "log 0.4.14",
"memmap2 0.5.2", "memmap2 0.5.2",
"modular-bitfield",
"rand 0.7.3", "rand 0.7.3",
"rayon", "rayon",
"solana-logger 1.10.0", "solana-logger 1.10.0",

View File

@ -17,6 +17,7 @@ log = { version = "0.4.11" }
solana-measure = { path = "../measure", version = "=1.10.0" } solana-measure = { path = "../measure", version = "=1.10.0" }
rand = "0.7.0" rand = "0.7.0"
tempfile = "3.3.0" tempfile = "3.3.0"
modular-bitfield = "0.11.2"
[dev-dependencies] [dev-dependencies]
fs_extra = "1.2.0" fs_extra = "1.2.0"

View File

@ -1,9 +1,11 @@
#![allow(dead_code)]
use { use {
crate::{ crate::{
bucket::Bucket, bucket::Bucket,
bucket_storage::{BucketStorage, Uid}, bucket_storage::{BucketStorage, Uid},
RefCount, RefCount,
}, },
modular_bitfield::prelude::*,
solana_sdk::{clock::Slot, pubkey::Pubkey}, solana_sdk::{clock::Slot, pubkey::Pubkey},
std::{ std::{
collections::hash_map::DefaultHasher, collections::hash_map::DefaultHasher,
@ -19,38 +21,40 @@ use {
pub struct IndexEntry { pub struct IndexEntry {
pub key: Pubkey, // can this be smaller if we have reduced the keys into buckets already? pub key: Pubkey, // can this be smaller if we have reduced the keys into buckets already?
pub ref_count: RefCount, // can this be smaller? Do we ever need more than 4B refcounts? pub ref_count: RefCount, // can this be smaller? Do we ever need more than 4B refcounts?
// storage_offset_mask contains both storage_offset and storage_capacity_when_created_pow2 storage_cap_and_offset: PackedStorage,
// see _MASK_ constants below
storage_offset_mask: u64, // smaller? since these are variably sized, this could get tricky. well, actually accountinfo is not variable sized...
// if the bucket doubled, the index can be recomputed using create_bucket_capacity_pow2 // if the bucket doubled, the index can be recomputed using create_bucket_capacity_pow2
pub num_slots: Slot, // can this be smaller? epoch size should ~ be the max len. this is the num elements in the slot list pub num_slots: Slot, // can this be smaller? epoch size should ~ be the max len. this is the num elements in the slot list
} }
/// how many bits to shift the capacity value in the mask /// Pack the storage offset and capacity-when-crated-pow2 fields into a single u64
const STORAGE_OFFSET_MASK_CAPACITY_SHIFT: u64 = (u64::BITS - u8::BITS) as u64; #[bitfield(bits = 64)]
/// mask to use on 'storage_offset_mask' to get the 'storage_offset' portion #[repr(C)]
const STORAGE_OFFSET_MASK_STORAGE_OFFSET: u64 = (1 << STORAGE_OFFSET_MASK_CAPACITY_SHIFT) - 1; #[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
struct PackedStorage {
capacity_when_created_pow2: B8,
offset: B56,
}
impl IndexEntry { impl IndexEntry {
pub fn init(&mut self, pubkey: &Pubkey) { pub fn init(&mut self, pubkey: &Pubkey) {
self.key = *pubkey; self.key = *pubkey;
self.ref_count = 0; self.ref_count = 0;
self.storage_offset_mask = 0; self.storage_cap_and_offset = PackedStorage::default();
self.num_slots = 0; self.num_slots = 0;
} }
pub fn set_storage_capacity_when_created_pow2( pub fn set_storage_capacity_when_created_pow2(
&mut self, &mut self,
storage_capacity_when_created_pow2: u8, storage_capacity_when_created_pow2: u8,
) { ) {
self.storage_offset_mask = self.storage_offset() self.storage_cap_and_offset
| ((storage_capacity_when_created_pow2 as u64) << STORAGE_OFFSET_MASK_CAPACITY_SHIFT) .set_capacity_when_created_pow2(storage_capacity_when_created_pow2)
} }
pub fn set_storage_offset(&mut self, storage_offset: u64) { pub fn set_storage_offset(&mut self, storage_offset: u64) {
let offset_mask = storage_offset & STORAGE_OFFSET_MASK_STORAGE_OFFSET; self.storage_cap_and_offset
assert_eq!(storage_offset, offset_mask, "offset too large"); .set_offset_checked(storage_offset)
self.storage_offset_mask = ((self.storage_capacity_when_created_pow2() as u64) .expect("New storage offset must fit into 7 bytes!")
<< STORAGE_OFFSET_MASK_CAPACITY_SHIFT)
| offset_mask;
} }
pub fn data_bucket_from_num_slots(num_slots: Slot) -> u64 { pub fn data_bucket_from_num_slots(num_slots: Slot) -> u64 {
@ -65,11 +69,12 @@ impl IndexEntry {
self.ref_count self.ref_count
} }
fn storage_offset(&self) -> u64 {
self.storage_offset_mask & STORAGE_OFFSET_MASK_STORAGE_OFFSET
}
fn storage_capacity_when_created_pow2(&self) -> u8 { fn storage_capacity_when_created_pow2(&self) -> u8 {
(self.storage_offset_mask >> STORAGE_OFFSET_MASK_CAPACITY_SHIFT) as u8 self.storage_cap_and_offset.capacity_when_created_pow2()
}
fn storage_offset(&self) -> u64 {
self.storage_cap_and_offset.offset()
} }
// This function maps the original data location into an index in the current bucket storage. // This function maps the original data location into an index in the current bucket storage.
@ -93,6 +98,7 @@ impl IndexEntry {
}; };
Some((slice, self.ref_count)) Some((slice, self.ref_count))
} }
pub fn key_uid(key: &Pubkey) -> Uid { pub fn key_uid(key: &Pubkey) -> Uid {
let mut s = DefaultHasher::new(); let mut s = DefaultHasher::new();
key.hash(&mut s); key.hash(&mut s);
@ -109,7 +115,7 @@ mod tests {
IndexEntry { IndexEntry {
key, key,
ref_count: 0, ref_count: 0,
storage_offset_mask: 0, storage_cap_and_offset: PackedStorage::default(),
num_slots: 0, num_slots: 0,
} }
} }
@ -133,4 +139,18 @@ mod tests {
} }
} }
} }
#[test]
fn test_size() {
assert_eq!(std::mem::size_of::<PackedStorage>(), 1 + 7);
assert_eq!(std::mem::size_of::<IndexEntry>(), 32 + 8 + 8 + 8);
}
#[test]
#[should_panic(expected = "New storage offset must fit into 7 bytes!")]
fn test_set_storage_offset_value_too_large() {
let too_big = 1 << 56;
let mut index = IndexEntry::new(Pubkey::new_unique());
index.set_storage_offset(too_big);
}
} }

View File

@ -1697,6 +1697,27 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "modular-bitfield"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74"
dependencies = [
"modular-bitfield-impl",
"static_assertions",
]
[[package]]
name = "modular-bitfield-impl"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789"
dependencies = [
"proc-macro2 1.0.24",
"quote 1.0.6",
"syn 1.0.67",
]
[[package]] [[package]]
name = "net2" name = "net2"
version = "0.2.37" version = "0.2.37"
@ -3044,6 +3065,7 @@ version = "1.10.0"
dependencies = [ dependencies = [
"log", "log",
"memmap2 0.5.2", "memmap2 0.5.2",
"modular-bitfield",
"rand 0.7.3", "rand 0.7.3",
"solana-measure", "solana-measure",
"solana-sdk", "solana-sdk",