cache sorted long and short term block weights

This commit is contained in:
Boog900 2023-11-08 00:28:15 +00:00
parent 9471ca5d6a
commit f025513950
No known key found for this signature in database
GPG key ID: 5401367FB7302004
3 changed files with 84 additions and 36 deletions

View file

@ -24,7 +24,7 @@ use monero_consensus::{
mod tx_pool; mod tx_pool;
const MAX_BLOCKS_IN_RANGE: u64 = 1000; const MAX_BLOCKS_IN_RANGE: u64 = 1000;
const MAX_BLOCKS_HEADERS_IN_RANGE: u64 = 250; const MAX_BLOCKS_HEADERS_IN_RANGE: u64 = 500;
/// Calls for a batch of blocks, returning the response and the time it took. /// Calls for a batch of blocks, returning the response and the time it took.
async fn call_batch<D: Database>( async fn call_batch<D: Database>(
@ -229,13 +229,14 @@ async fn main() {
let rpc_config = RpcConfig::new(MAX_BLOCKS_IN_RANGE, MAX_BLOCKS_HEADERS_IN_RANGE); let rpc_config = RpcConfig::new(MAX_BLOCKS_IN_RANGE, MAX_BLOCKS_HEADERS_IN_RANGE);
let rpc_config = Arc::new(RwLock::new(rpc_config)); let rpc_config = Arc::new(RwLock::new(rpc_config));
tracing::info!("Attempting to open cache at: {}", file_for_cache.display());
let cache = match ScanningCache::load(&file_for_cache) { let cache = match ScanningCache::load(&file_for_cache) {
Ok(cache) => { Ok(cache) => {
tracing::info!("Reloaded from cache, chain height: {}", cache.height); tracing::info!("Reloaded from cache, chain height: {}", cache.height);
Arc::new(RwLock::new(cache)) Arc::new(RwLock::new(cache))
} }
Err(_) => { Err(_) => {
tracing::info!("Couldn't load from cache starting from scratch"); tracing::warn!("Couldn't load from cache starting from scratch");
let mut cache = ScanningCache::default(); let mut cache = ScanningCache::default();
let genesis = monero_consensus::genesis::generate_genesis_block(&network); let genesis = monero_consensus::genesis::generate_genesis_block(&network);

View file

@ -297,12 +297,10 @@ impl Service<BlockChainContextRequest> for BlockChainContextService {
next_difficulty: difficulty_cache.next_difficulty(&current_hf), next_difficulty: difficulty_cache.next_difficulty(&current_hf),
cumulative_difficulty: difficulty_cache.cumulative_difficulty(), cumulative_difficulty: difficulty_cache.cumulative_difficulty(),
effective_median_weight: weight_cache effective_median_weight: weight_cache
.effective_median_block_weight(&current_hf) .effective_median_block_weight(&current_hf),
.await, median_long_term_weight: weight_cache.median_long_term_weight(),
median_long_term_weight: weight_cache.median_long_term_weight().await,
median_weight_for_block_reward: weight_cache median_weight_for_block_reward: weight_cache
.median_for_block_reward(&current_hf) .median_for_block_reward(&current_hf),
.await,
already_generated_coins: *already_generated_coins, already_generated_coins: *already_generated_coins,
top_block_timestamp: difficulty_cache.top_block_timestamp(), top_block_timestamp: difficulty_cache.top_block_timestamp(),
median_block_timestamp: difficulty_cache.median_timestamp( median_block_timestamp: difficulty_cache.median_timestamp(

View file

@ -77,6 +77,14 @@ impl BlockWeightsCacheConfig {
pub struct BlockWeightsCache { pub struct BlockWeightsCache {
short_term_block_weights: VecDeque<usize>, short_term_block_weights: VecDeque<usize>,
long_term_weights: VecDeque<usize>, long_term_weights: VecDeque<usize>,
/// The short term block weights sorted so we don't have to sort them every time we need
/// the median.
cached_sorted_long_term_weights: Vec<usize>,
/// The long term block weights sorted so we don't have to sort them every time we need
/// the median.
cached_sorted_short_term_weights: Vec<usize>,
/// The height of the top block. /// The height of the top block.
tip_height: u64, tip_height: u64,
@ -93,25 +101,37 @@ impl BlockWeightsCache {
) -> Result<Self, ConsensusError> { ) -> Result<Self, ConsensusError> {
tracing::info!("Initializing weight cache this may take a while."); tracing::info!("Initializing weight cache this may take a while.");
let long_term_weights: VecDeque<usize> = get_long_term_weight_in_range( let long_term_weights = get_long_term_weight_in_range(
chain_height.saturating_sub(config.long_term_window)..chain_height, chain_height.saturating_sub(config.long_term_window)..chain_height,
database.clone(), database.clone(),
) )
.await? .await?;
.into();
let short_term_block_weights: VecDeque<usize> = get_blocks_weight_in_range( let short_term_block_weights = get_blocks_weight_in_range(
chain_height.saturating_sub(config.short_term_window)..chain_height, chain_height.saturating_sub(config.short_term_window)..chain_height,
database, database,
) )
.await? .await?;
.into();
tracing::info!("Initialized block weight cache, chain-height: {:?}, long term weights length: {:?}, short term weights length: {:?}", chain_height, long_term_weights.len(), short_term_block_weights.len()); tracing::info!("Initialized block weight cache, chain-height: {:?}, long term weights length: {:?}, short term weights length: {:?}", chain_height, long_term_weights.len(), short_term_block_weights.len());
let mut cloned_short_term_weights = short_term_block_weights.clone();
let mut cloned_long_term_weights = long_term_weights.clone();
Ok(BlockWeightsCache { Ok(BlockWeightsCache {
short_term_block_weights, short_term_block_weights: short_term_block_weights.into(),
long_term_weights, long_term_weights: long_term_weights.into(),
cached_sorted_long_term_weights: rayon_spawn_async(|| {
cloned_long_term_weights.par_sort_unstable();
cloned_long_term_weights
})
.await,
cached_sorted_short_term_weights: rayon_spawn_async(|| {
cloned_short_term_weights.par_sort_unstable();
cloned_short_term_weights
})
.await,
tip_height: chain_height - 1, tip_height: chain_height - 1,
config, config,
}) })
@ -132,59 +152,88 @@ impl BlockWeightsCache {
); );
self.long_term_weights.push_back(long_term_weight); self.long_term_weights.push_back(long_term_weight);
match self
.cached_sorted_long_term_weights
.binary_search(&long_term_weight)
{
Ok(idx) | Err(idx) => self
.cached_sorted_long_term_weights
.insert(idx, long_term_weight),
}
if u64::try_from(self.long_term_weights.len()).unwrap() > self.config.long_term_window { if u64::try_from(self.long_term_weights.len()).unwrap() > self.config.long_term_window {
self.long_term_weights.pop_front(); let val = self
.long_term_weights
.pop_front()
.expect("long term window can't be negative");
match self.cached_sorted_long_term_weights.binary_search(&val) {
Ok(idx) | Err(idx) => self.cached_sorted_long_term_weights.remove(idx),
};
} }
self.short_term_block_weights.push_back(block_weight); self.short_term_block_weights.push_back(block_weight);
match self
.cached_sorted_short_term_weights
.binary_search(&long_term_weight)
{
Ok(idx) | Err(idx) => self
.cached_sorted_short_term_weights
.insert(idx, long_term_weight),
}
if u64::try_from(self.short_term_block_weights.len()).unwrap() if u64::try_from(self.short_term_block_weights.len()).unwrap()
> self.config.short_term_window > self.config.short_term_window
{ {
self.short_term_block_weights.pop_front(); let val = self
.short_term_block_weights
.pop_front()
.expect("short term window can't be negative");
match self.cached_sorted_short_term_weights.binary_search(&val) {
Ok(idx) | Err(idx) => self.cached_sorted_short_term_weights.remove(idx),
};
} }
debug_assert_eq!(
self.cached_sorted_long_term_weights.len(),
self.long_term_weights.len()
);
debug_assert_eq!(
self.cached_sorted_short_term_weights.len(),
self.short_term_block_weights.len()
);
} }
/// Returns the median long term weight over the last [`LONG_TERM_WINDOW`] blocks, or custom amount of blocks in the config. /// Returns the median long term weight over the last [`LONG_TERM_WINDOW`] blocks, or custom amount of blocks in the config.
pub async fn median_long_term_weight(&self) -> usize { pub fn median_long_term_weight(&self) -> usize {
let mut sorted_long_term_weights: Vec<usize> = self.long_term_weights.clone().into(); median(&self.cached_sorted_long_term_weights)
// Move this out of the async runtime as this can take a bit.
let sorted_long_term_weights = rayon_spawn_async(|| {
sorted_long_term_weights.par_sort_unstable();
sorted_long_term_weights
})
.await;
median(&sorted_long_term_weights)
} }
pub fn median_short_term_weight(&self) -> usize { pub fn median_short_term_weight(&self) -> usize {
let mut sorted_short_term_block_weights: Vec<usize> = median(&self.cached_sorted_short_term_weights)
self.short_term_block_weights.clone().into();
sorted_short_term_block_weights.sort_unstable();
median(&sorted_short_term_block_weights)
} }
/// Returns the effective median weight, used for block reward calculations and to calculate /// Returns the effective median weight, used for block reward calculations and to calculate
/// the block weight limit. /// the block weight limit.
/// ///
/// See: https://cuprate.github.io/monero-book/consensus_rules/blocks/weight_limit.html#calculating-effective-median-weight /// See: https://cuprate.github.io/monero-book/consensus_rules/blocks/weight_limit.html#calculating-effective-median-weight
pub async fn effective_median_block_weight(&self, hf: &HardFork) -> usize { pub fn effective_median_block_weight(&self, hf: &HardFork) -> usize {
calculate_effective_median_block_weight( calculate_effective_median_block_weight(
hf, hf,
self.median_short_term_weight(), self.median_short_term_weight(),
self.median_long_term_weight().await, self.median_long_term_weight(),
) )
} }
/// Returns the median weight used to calculate block reward punishment. /// Returns the median weight used to calculate block reward punishment.
/// ///
/// https://cuprate.github.io/monero-book/consensus_rules/blocks/reward.html#calculating-block-reward /// https://cuprate.github.io/monero-book/consensus_rules/blocks/reward.html#calculating-block-reward
pub async fn median_for_block_reward(&self, hf: &HardFork) -> usize { pub fn median_for_block_reward(&self, hf: &HardFork) -> usize {
if hf.in_range(&HardFork::V1, &HardFork::V12) { if hf.in_range(&HardFork::V1, &HardFork::V12) {
self.median_short_term_weight() self.median_short_term_weight()
} else { } else {
self.effective_median_block_weight(hf).await self.effective_median_block_weight(hf)
} }
.max(penalty_free_zone(hf)) .max(penalty_free_zone(hf))
} }