Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.

Commit db8811e

Browse files
support bin divisions up to 65536 (#17563)
* support bin divisions up to 65536 * add tests
1 parent 8e1e57a commit db8811e

File tree

3 files changed

+147
-21
lines changed

3 files changed

+147
-21
lines changed

runtime/src/accounts_db.rs

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use crate::{
2929
ancestors::Ancestors,
3030
append_vec::{AppendVec, StoredAccountMeta, StoredMeta, StoredMetaWriteVersion},
3131
contains::Contains,
32+
pubkey_bins::PubkeyBinCalculator16,
3233
read_only_accounts_cache::ReadOnlyAccountsCache,
3334
sorted_storages::SortedStorages,
3435
};
@@ -4434,8 +4435,7 @@ impl AccountsDb {
44344435
bin_range: &Range<usize>,
44354436
check_hash: bool,
44364437
) -> Result<Vec<Vec<Vec<CalculateHashIntermediate>>>, BankHashVerificationError> {
4437-
let max_plus_1 = std::u8::MAX as usize + 1;
4438-
assert!(bins <= max_plus_1 && bins > 0);
4438+
let bin_calculator = PubkeyBinCalculator16::new(bins);
44394439
assert!(bin_range.start < bins && bin_range.end <= bins && bin_range.start < bin_range.end);
44404440
let mut time = Measure::start("scan all accounts");
44414441
stats.num_snapshot_storage = storage.len();
@@ -4447,7 +4447,7 @@ impl AccountsDb {
44474447
accum: &mut Vec<Vec<CalculateHashIntermediate>>,
44484448
slot: Slot| {
44494449
let pubkey = loaded_account.pubkey();
4450-
let pubkey_to_bin_index = pubkey.as_ref()[0] as usize * bins / max_plus_1;
4450+
let pubkey_to_bin_index = bin_calculator.bin_from_pubkey(pubkey);
44514451
if !bin_range.contains(&pubkey_to_bin_index) {
44524452
return;
44534453
}
@@ -5852,24 +5852,6 @@ pub mod tests {
58525852
SortedStorages::new(&[])
58535853
}
58545854

5855-
#[test]
5856-
#[should_panic(expected = "assertion failed: bins <= max_plus_1 && bins > 0")]
5857-
fn test_accountsdb_scan_snapshot_stores_illegal_bins2() {
5858-
let mut stats = HashStats::default();
5859-
let bounds = Range { start: 0, end: 0 };
5860-
5861-
AccountsDb::scan_snapshot_stores(&empty_storages(), &mut stats, 257, &bounds, false)
5862-
.unwrap();
5863-
}
5864-
#[test]
5865-
#[should_panic(expected = "assertion failed: bins <= max_plus_1 && bins > 0")]
5866-
fn test_accountsdb_scan_snapshot_stores_illegal_bins() {
5867-
let mut stats = HashStats::default();
5868-
let bounds = Range { start: 0, end: 0 };
5869-
5870-
AccountsDb::scan_snapshot_stores(&empty_storages(), &mut stats, 0, &bounds, false).unwrap();
5871-
}
5872-
58735855
#[test]
58745856
#[should_panic(
58755857
expected = "bin_range.start < bins && bin_range.end <= bins &&\\n bin_range.start < bin_range.end"

runtime/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub mod log_collector;
2828
pub mod message_processor;
2929
mod native_loader;
3030
pub mod non_circulating_supply;
31+
mod pubkey_bins;
3132
mod read_only_accounts_cache;
3233
pub mod rent_collector;
3334
pub mod secondary_index;

runtime/src/pubkey_bins.rs

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
use solana_sdk::pubkey::Pubkey;
2+
3+
pub(crate) struct PubkeyBinCalculator16 {
4+
// how many bits from the first 2 bytes to shift away to ignore when calculating bin
5+
shift_bits: u32,
6+
}
7+
8+
impl PubkeyBinCalculator16 {
9+
const fn num_bits<T>() -> usize {
10+
std::mem::size_of::<T>() * 8
11+
}
12+
13+
fn log_2(x: u32) -> u32 {
14+
assert!(x > 0);
15+
Self::num_bits::<u32>() as u32 - x.leading_zeros() - 1
16+
}
17+
18+
pub fn new(bins: usize) -> Self {
19+
const MAX_BITS: u32 = 16;
20+
assert!(bins > 0);
21+
let max_plus_1 = 1 << MAX_BITS;
22+
assert!(bins <= max_plus_1);
23+
assert!(bins.is_power_of_two());
24+
let bits = Self::log_2(bins as u32);
25+
Self {
26+
shift_bits: MAX_BITS - bits,
27+
}
28+
}
29+
30+
pub fn bin_from_pubkey(&self, pubkey: &Pubkey) -> usize {
31+
let as_ref = pubkey.as_ref();
32+
((as_ref[0] as usize * 256 + as_ref[1] as usize) as usize) >> self.shift_bits
33+
}
34+
}
35+
36+
#[cfg(test)]
37+
pub mod tests {
38+
use super::*;
39+
40+
#[test]
41+
fn test_pubkey_bins_log2() {
42+
assert_eq!(PubkeyBinCalculator16::num_bits::<u8>(), 8);
43+
assert_eq!(PubkeyBinCalculator16::num_bits::<u32>(), 32);
44+
for i in 0..32 {
45+
assert_eq!(PubkeyBinCalculator16::log_2(2u32.pow(i)), i);
46+
}
47+
}
48+
49+
#[test]
50+
fn test_pubkey_bins() {
51+
for i in 0..=16 {
52+
let bins = 2u32.pow(i);
53+
let calc = PubkeyBinCalculator16::new(bins as usize);
54+
assert_eq!(calc.shift_bits, 16 - i, "i: {}", i);
55+
}
56+
}
57+
58+
#[test]
59+
fn test_pubkey_bins_pubkeys() {
60+
let mut pk = Pubkey::new(&[0; 32]);
61+
for i in 0..=8 {
62+
let bins = 2usize.pow(i);
63+
let calc = PubkeyBinCalculator16::new(bins);
64+
65+
let shift_bits = calc.shift_bits - 8; // we are only dealing with first byte
66+
67+
pk.as_mut()[0] = 0;
68+
assert_eq!(0, calc.bin_from_pubkey(&pk));
69+
pk.as_mut()[0] = 0xff;
70+
assert_eq!(bins - 1, calc.bin_from_pubkey(&pk));
71+
72+
for bin in 0..bins {
73+
pk.as_mut()[0] = (bin << shift_bits) as u8;
74+
assert_eq!(
75+
bin,
76+
calc.bin_from_pubkey(&pk),
77+
"bin: {}/{}, bits: {}, val: {}",
78+
bin,
79+
bins,
80+
shift_bits,
81+
pk.as_ref()[0]
82+
);
83+
if bin > 0 {
84+
pk.as_mut()[0] = ((bin << shift_bits) - 1) as u8;
85+
assert_eq!(bin - 1, calc.bin_from_pubkey(&pk));
86+
}
87+
}
88+
}
89+
90+
for i in 9..=16 {
91+
let mut pk = Pubkey::new(&[0; 32]);
92+
let bins = 2usize.pow(i);
93+
let calc = PubkeyBinCalculator16::new(bins);
94+
95+
let shift_bits = calc.shift_bits;
96+
97+
pk.as_mut()[1] = 0;
98+
assert_eq!(0, calc.bin_from_pubkey(&pk));
99+
pk.as_mut()[0] = 0xff;
100+
pk.as_mut()[1] = 0xff;
101+
assert_eq!(bins - 1, calc.bin_from_pubkey(&pk));
102+
103+
let mut pk = Pubkey::new(&[0; 32]);
104+
for bin in 0..bins {
105+
let mut target = (bin << shift_bits) as u16;
106+
pk.as_mut()[0] = (target / 256) as u8;
107+
pk.as_mut()[1] = (target % 256) as u8;
108+
assert_eq!(
109+
bin,
110+
calc.bin_from_pubkey(&pk),
111+
"bin: {}/{}, bits: {}, val: {}",
112+
bin,
113+
bins,
114+
shift_bits,
115+
pk.as_ref()[0]
116+
);
117+
if bin > 0 {
118+
target -= 1;
119+
pk.as_mut()[0] = (target / 256) as u8;
120+
pk.as_mut()[1] = (target % 256) as u8;
121+
assert_eq!(bin - 1, calc.bin_from_pubkey(&pk));
122+
}
123+
}
124+
}
125+
}
126+
127+
#[test]
128+
#[should_panic(expected = "bins.is_power_of_two()")]
129+
fn test_pubkey_bins_illegal_bins3() {
130+
PubkeyBinCalculator16::new(3);
131+
}
132+
133+
#[test]
134+
#[should_panic(expected = "bins <= max_plus_1")]
135+
fn test_pubkey_bins_illegal_bins2() {
136+
PubkeyBinCalculator16::new(65537);
137+
}
138+
#[test]
139+
#[should_panic(expected = "bins > 0")]
140+
fn test_pubkey_bins_illegal_bins() {
141+
PubkeyBinCalculator16::new(0);
142+
}
143+
}

0 commit comments

Comments
 (0)