@@ -16,6 +16,7 @@ use crate::{
16
16
stores:: {
17
17
acceptance_data:: { AcceptanceDataStoreReader , DbAcceptanceDataStore } ,
18
18
block_transactions:: { BlockTransactionsStoreReader , DbBlockTransactionsStore } ,
19
+ block_window_cache:: BlockWindowCacheStore ,
19
20
daa:: DbDaaStore ,
20
21
depth:: { DbDepthStore , DepthStoreReader } ,
21
22
ghostdag:: { DbGhostdagStore , GhostdagData , GhostdagStoreReader } ,
@@ -76,6 +77,7 @@ use kaspa_database::prelude::{StoreError, StoreResultEmptyTuple, StoreResultExte
76
77
use kaspa_hashes:: Hash ;
77
78
use kaspa_muhash:: MuHash ;
78
79
use kaspa_notify:: { events:: EventType , notifier:: Notify } ;
80
+ use once_cell:: unsync:: Lazy ;
79
81
80
82
use super :: errors:: { PruningImportError , PruningImportResult } ;
81
83
use crossbeam_channel:: { Receiver as CrossbeamReceiver , Sender as CrossbeamSender } ;
@@ -149,6 +151,10 @@ pub struct VirtualStateProcessor {
149
151
pub ( super ) parents_manager : DbParentsManager ,
150
152
pub ( super ) depth_manager : DbBlockDepthManager ,
151
153
154
+ // block window caches
155
+ pub ( super ) block_window_cache_for_difficulty : Arc < BlockWindowCacheStore > ,
156
+ pub ( super ) block_window_cache_for_past_median_time : Arc < BlockWindowCacheStore > ,
157
+
152
158
// Pruning lock
153
159
pruning_lock : SessionLock ,
154
160
@@ -206,6 +212,9 @@ impl VirtualStateProcessor {
206
212
pruning_utxoset_stores : storage. pruning_utxoset_stores . clone ( ) ,
207
213
lkg_virtual_state : storage. lkg_virtual_state . clone ( ) ,
208
214
215
+ block_window_cache_for_difficulty : storage. block_window_cache_for_difficulty . clone ( ) ,
216
+ block_window_cache_for_past_median_time : storage. block_window_cache_for_past_median_time . clone ( ) ,
217
+
209
218
ghostdag_manager : services. ghostdag_manager . clone ( ) ,
210
219
reachability_service : services. reachability_service . clone ( ) ,
211
220
relations_service : services. relations_service . clone ( ) ,
@@ -291,6 +300,10 @@ impl VirtualStateProcessor {
291
300
292
301
let sink_multiset = self . utxo_multisets_store . get ( new_sink) . unwrap ( ) ;
293
302
let chain_path = self . dag_traversal_manager . calculate_chain_path ( prev_sink, new_sink, None ) ;
303
+ let sink_ghostdag_data = Lazy :: new ( || self . ghostdag_store . get_data ( new_sink) . unwrap ( ) ) ;
304
+ // Cache the DAA and Median time windows of the sink for future use, as well as prepare for virtual's window calculations
305
+ self . cache_sink_windows ( new_sink, prev_sink, & sink_ghostdag_data) ;
306
+
294
307
let new_virtual_state = self
295
308
. calculate_and_commit_virtual_state (
296
309
virtual_read,
@@ -302,12 +315,19 @@ impl VirtualStateProcessor {
302
315
)
303
316
. expect ( "all possible rule errors are unexpected here" ) ;
304
317
318
+ let compact_sink_ghostdag_data = if let Some ( sink_ghostdag_data) = Lazy :: get ( & sink_ghostdag_data) {
319
+ // If we had to retrieve the full data, we convert it to compact
320
+ sink_ghostdag_data. to_compact ( )
321
+ } else {
322
+ // Else we query the compact data directly.
323
+ self . ghostdag_store . get_compact_data ( new_sink) . unwrap ( )
324
+ } ;
325
+
305
326
// Update the pruning processor about the virtual state change
306
- let sink_ghostdag_data = self . ghostdag_store . get_compact_data ( new_sink) . unwrap ( ) ;
307
327
// Empty the channel before sending the new message. If pruning processor is busy, this step makes sure
308
328
// the internal channel does not grow with no need (since we only care about the most recent message)
309
329
let _consume = self . pruning_receiver . try_iter ( ) . count ( ) ;
310
- self . pruning_sender . send ( PruningProcessingMessage :: Process { sink_ghostdag_data } ) . unwrap ( ) ;
330
+ self . pruning_sender . send ( PruningProcessingMessage :: Process { sink_ghostdag_data : compact_sink_ghostdag_data } ) . unwrap ( ) ;
311
331
312
332
// Emit notifications
313
333
let accumulated_diff = Arc :: new ( accumulated_diff) ;
@@ -319,7 +339,7 @@ impl VirtualStateProcessor {
319
339
. notify ( Notification :: UtxosChanged ( UtxosChangedNotification :: new ( accumulated_diff, virtual_parents) ) )
320
340
. expect ( "expecting an open unbounded channel" ) ;
321
341
self . notification_root
322
- . notify ( Notification :: SinkBlueScoreChanged ( SinkBlueScoreChangedNotification :: new ( sink_ghostdag_data . blue_score ) ) )
342
+ . notify ( Notification :: SinkBlueScoreChanged ( SinkBlueScoreChangedNotification :: new ( compact_sink_ghostdag_data . blue_score ) ) )
323
343
. expect ( "expecting an open unbounded channel" ) ;
324
344
self . notification_root
325
345
. notify ( Notification :: VirtualDaaScoreChanged ( VirtualDaaScoreChangedNotification :: new ( new_virtual_state. daa_score ) ) )
@@ -540,6 +560,26 @@ impl VirtualStateProcessor {
540
560
drop ( selected_chain_write) ;
541
561
}
542
562
563
+ /// Caches the DAA and Median time windows of the sink block (if needed). Following, virtual's window calculations will
564
+ /// naturally hit the cache finding the sink's windows and building upon them.
565
+ fn cache_sink_windows ( & self , new_sink : Hash , prev_sink : Hash , sink_ghostdag_data : & impl Deref < Target = Arc < GhostdagData > > ) {
566
+ // We expect that the `new_sink` is cached (or some close-enough ancestor thereof) if it is equal to the `prev_sink`,
567
+ // Hence we short-circuit the check of the keys in such cases, thereby reducing the access of the read-lock
568
+ if new_sink != prev_sink {
569
+ // this is only important for ibd performance, as we incur expensive cache misses otherwise.
570
+ // this occurs because we cannot rely on header processing to pre-cache in this scenario.
571
+ if !self . block_window_cache_for_difficulty . contains_key ( & new_sink) {
572
+ self . block_window_cache_for_difficulty
573
+ . insert ( new_sink, self . window_manager . block_daa_window ( sink_ghostdag_data. deref ( ) ) . unwrap ( ) . window ) ;
574
+ } ;
575
+
576
+ if !self . block_window_cache_for_past_median_time . contains_key ( & new_sink) {
577
+ self . block_window_cache_for_past_median_time
578
+ . insert ( new_sink, self . window_manager . calc_past_median_time ( sink_ghostdag_data. deref ( ) ) . unwrap ( ) . 1 ) ;
579
+ } ;
580
+ }
581
+ }
582
+
543
583
/// Returns the max number of tips to consider as virtual parents in a single virtual resolve operation.
544
584
///
545
585
/// Guaranteed to be `>= self.max_block_parents`
0 commit comments