Make slot history a billion times faster (#10175)

This commit is contained in:
sakridge 2020-05-22 11:15:16 -07:00 committed by GitHub
parent b7a32f01c0
commit 2324eb9ff9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 134 additions and 4 deletions

View File

@ -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;
}
});
}

View File

@ -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);
}
}