Make slot history a billion times faster (#10175)
This commit is contained in:
parent
b7a32f01c0
commit
2324eb9ff9
|
@ -13,3 +13,16 @@ fn bench_to_from_account(b: &mut Bencher) {
|
|||
slot_history = SlotHistory::from_account(&account).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_slot_history_add_new(b: &mut Bencher) {
|
||||
let mut slot_history = SlotHistory::default();
|
||||
|
||||
let mut slot = 0;
|
||||
b.iter(|| {
|
||||
for _ in 0..5 {
|
||||
slot_history.add(slot);
|
||||
slot += 100000;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
//!
|
||||
pub use crate::clock::Slot;
|
||||
use bv::BitVec;
|
||||
use bv::BitsMut;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
#[derive(Serialize, Deserialize, PartialEq)]
|
||||
pub struct SlotHistory {
|
||||
pub bits: BitVec<u64>,
|
||||
pub next_slot: Slot,
|
||||
|
@ -19,6 +20,20 @@ impl Default for SlotHistory {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for SlotHistory {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "SlotHistory {{ slot: {} bits:", self.next_slot)?;
|
||||
for i in 0..MAX_ENTRIES {
|
||||
if self.bits.get(i) {
|
||||
write!(f, "1")?;
|
||||
} else {
|
||||
write!(f, "0")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub const MAX_ENTRIES: u64 = 1024 * 1024; // 1 million slots is about 5 days
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
|
@ -31,8 +46,17 @@ pub enum Check {
|
|||
|
||||
impl SlotHistory {
|
||||
pub fn add(&mut self, slot: Slot) {
|
||||
for skipped in self.next_slot..slot {
|
||||
self.bits.set(skipped % MAX_ENTRIES, false);
|
||||
if slot > self.next_slot && slot - self.next_slot >= MAX_ENTRIES {
|
||||
// Wrapped past current history,
|
||||
// clear entire bitvec.
|
||||
let full_blocks = (MAX_ENTRIES as usize) / 64;
|
||||
for i in 0..full_blocks {
|
||||
self.bits.set_block(i, 0);
|
||||
}
|
||||
} else {
|
||||
for skipped in self.next_slot..slot {
|
||||
self.bits.set(skipped % MAX_ENTRIES, false);
|
||||
}
|
||||
}
|
||||
self.bits.set(slot % MAX_ENTRIES, true);
|
||||
self.next_slot = slot + 1;
|
||||
|
@ -54,19 +78,112 @@ impl SlotHistory {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use log::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
fn slot_history_test1() {
|
||||
solana_logger::setup();
|
||||
// should be divisable by 64 since the clear logic works on blocks
|
||||
assert_eq!(MAX_ENTRIES % 64, 0);
|
||||
let mut slot_history = SlotHistory::default();
|
||||
info!("add 2");
|
||||
slot_history.add(2);
|
||||
assert_eq!(slot_history.check(0), Check::Found);
|
||||
assert_eq!(slot_history.check(1), Check::NotFound);
|
||||
for i in 3..MAX_ENTRIES {
|
||||
assert_eq!(slot_history.check(i), Check::Future);
|
||||
}
|
||||
info!("add 20");
|
||||
slot_history.add(20);
|
||||
info!("add max_entries");
|
||||
slot_history.add(MAX_ENTRIES);
|
||||
assert_eq!(slot_history.check(0), Check::TooOld);
|
||||
assert_eq!(slot_history.check(1), Check::NotFound);
|
||||
assert_eq!(slot_history.check(2), Check::Found);
|
||||
assert_eq!(slot_history.check(20), Check::Found);
|
||||
assert_eq!(slot_history.check(MAX_ENTRIES), Check::Found);
|
||||
for i in 3..20 {
|
||||
assert_eq!(slot_history.check(i), Check::NotFound, "i: {}", i);
|
||||
}
|
||||
for i in 21..MAX_ENTRIES {
|
||||
assert_eq!(slot_history.check(i), Check::NotFound, "i: {}", i);
|
||||
}
|
||||
assert_eq!(slot_history.check(MAX_ENTRIES + 1), Check::Future);
|
||||
|
||||
info!("add max_entries + 3");
|
||||
let slot = 3 * MAX_ENTRIES + 3;
|
||||
slot_history.add(slot);
|
||||
assert_eq!(slot_history.check(0), Check::TooOld);
|
||||
assert_eq!(slot_history.check(1), Check::TooOld);
|
||||
assert_eq!(slot_history.check(2), Check::TooOld);
|
||||
assert_eq!(slot_history.check(20), Check::TooOld);
|
||||
assert_eq!(slot_history.check(21), Check::TooOld);
|
||||
assert_eq!(slot_history.check(MAX_ENTRIES), Check::TooOld);
|
||||
let start = slot - MAX_ENTRIES + 1;
|
||||
let end = slot;
|
||||
for i in start..end {
|
||||
assert_eq!(slot_history.check(i), Check::NotFound, "i: {}", i);
|
||||
}
|
||||
assert_eq!(slot_history.check(slot), Check::Found);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slot_history_test_wrap() {
|
||||
solana_logger::setup();
|
||||
let mut slot_history = SlotHistory::default();
|
||||
info!("add 2");
|
||||
slot_history.add(2);
|
||||
assert_eq!(slot_history.check(0), Check::Found);
|
||||
assert_eq!(slot_history.check(1), Check::NotFound);
|
||||
for i in 3..MAX_ENTRIES {
|
||||
assert_eq!(slot_history.check(i), Check::Future);
|
||||
}
|
||||
info!("add 20");
|
||||
slot_history.add(20);
|
||||
info!("add max_entries + 19");
|
||||
slot_history.add(MAX_ENTRIES + 19);
|
||||
for i in 0..19 {
|
||||
assert_eq!(slot_history.check(i), Check::TooOld);
|
||||
}
|
||||
assert_eq!(slot_history.check(MAX_ENTRIES), Check::NotFound);
|
||||
assert_eq!(slot_history.check(20), Check::Found);
|
||||
assert_eq!(slot_history.check(MAX_ENTRIES + 19), Check::Found);
|
||||
assert_eq!(slot_history.check(20), Check::Found);
|
||||
for i in 21..MAX_ENTRIES + 19 {
|
||||
assert_eq!(slot_history.check(i), Check::NotFound, "found: {}", i);
|
||||
}
|
||||
assert_eq!(slot_history.check(MAX_ENTRIES + 20), Check::Future);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slot_history_test_same_index() {
|
||||
solana_logger::setup();
|
||||
let mut slot_history = SlotHistory::default();
|
||||
info!("add 3,4");
|
||||
slot_history.add(3);
|
||||
slot_history.add(4);
|
||||
assert_eq!(slot_history.check(1), Check::NotFound);
|
||||
assert_eq!(slot_history.check(2), Check::NotFound);
|
||||
assert_eq!(slot_history.check(3), Check::Found);
|
||||
assert_eq!(slot_history.check(4), Check::Found);
|
||||
slot_history.add(MAX_ENTRIES + 5);
|
||||
assert_eq!(slot_history.check(5), Check::TooOld);
|
||||
for i in 6..MAX_ENTRIES + 5 {
|
||||
assert_eq!(slot_history.check(i), Check::NotFound, "i: {}", i);
|
||||
}
|
||||
assert_eq!(slot_history.check(MAX_ENTRIES + 5), Check::Found);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_older_slot() {
|
||||
let mut slot_history = SlotHistory::default();
|
||||
slot_history.add(10);
|
||||
slot_history.add(5);
|
||||
assert_eq!(slot_history.check(0), Check::Found);
|
||||
assert_eq!(slot_history.check(5), Check::Found);
|
||||
// If we go backwards we reset?
|
||||
assert_eq!(slot_history.check(10), Check::Future);
|
||||
assert_eq!(slot_history.check(6), Check::Future);
|
||||
assert_eq!(slot_history.check(11), Check::Future);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue