From b7ba41e163feaf2b9bdc8bd82c4d7e446aa8d542 Mon Sep 17 00:00:00 2001 From: AvivYossef-starkware Date: Sun, 23 Feb 2025 16:59:06 +0200 Subject: [PATCH] feat(blockifier): add cairo native cache rate metric --- Cargo.lock | 1 + Monitoring/sequencer/dev_grafana.json | 7 +++ .../src/state/contract_class_manager.rs | 9 +++ .../src/state/native_class_manager.rs | 55 ++++++++++++++++++- .../src/state/native_class_manager_test.rs | 2 + crates/starknet_batcher/src/batcher.rs | 4 ++ crates/starknet_batcher/src/batcher_test.rs | 6 +- crates/starknet_batcher/src/block_builder.rs | 12 ++++ crates/starknet_batcher/src/metrics.rs | 4 ++ .../starknet_sequencer_dashboard/Cargo.toml | 1 + .../src/dashboard_definitions.rs | 17 +++++- 11 files changed, 114 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e1f69e3b5a2..d140c275c6f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11517,6 +11517,7 @@ name = "starknet_sequencer_dashboard" version = "0.0.0" dependencies = [ "colored 3.0.0", + "const_format", "indexmap 2.7.0", "serde", "serde_json", diff --git a/Monitoring/sequencer/dev_grafana.json b/Monitoring/sequencer/dev_grafana.json index 979c7a09098..51d3586910b 100644 --- a/Monitoring/sequencer/dev_grafana.json +++ b/Monitoring/sequencer/dev_grafana.json @@ -28,6 +28,13 @@ "type": "stat", "expr": "batcher_batched_transactions", "extra_params": {} + }, + { + "title": "cairo_native_cache_miss_ratio", + "description": "The ratio of cache misses in the Cairo native cache", + "type": "graph", + "expr": "100 * (class_cache_misses / clamp_min((class_cache_misses + class_cache_hits), 1))", + "extra_params": {} } ], "Http Server": [ diff --git a/crates/blockifier/src/state/contract_class_manager.rs b/crates/blockifier/src/state/contract_class_manager.rs index f47fc2b2183..f7328847982 100644 --- a/crates/blockifier/src/state/contract_class_manager.rs +++ b/crates/blockifier/src/state/contract_class_manager.rs @@ -44,6 +44,15 @@ pub mod trivial_class_manager { pub fn get_cache_size(&self) -> usize { self.cache.lock().cache_size() } + + // TODO(Aviv): consider support cache miss metrics. + pub fn take_cache_miss_counter(&self) -> u64 { + 0 + } + + pub fn take_cache_hit_counter(&self) -> u64 { + 0 + } } } diff --git a/crates/blockifier/src/state/native_class_manager.rs b/crates/blockifier/src/state/native_class_manager.rs index 9f666af8c8c..8e27bb5b968 100644 --- a/crates/blockifier/src/state/native_class_manager.rs +++ b/crates/blockifier/src/state/native_class_manager.rs @@ -1,3 +1,4 @@ +use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::mpsc::{sync_channel, Receiver, SyncSender, TrySendError}; use std::sync::Arc; @@ -49,6 +50,28 @@ pub struct NativeClassManager { sender: Option>, /// The sierra-to-native compiler. compiler: Option>, + /// cache_miss_rate + cache_metrics: Arc, +} + +#[derive(Default)] +pub struct CacheMetrics { + pub cache_misses: AtomicU64, + pub cache_hits: AtomicU64, +} + +impl CacheMetrics { + pub fn new() -> Self { + Self { cache_misses: AtomicU64::new(0), cache_hits: AtomicU64::new(0) } + } + + pub fn record_miss(&self) { + self.cache_misses.fetch_add(1, Ordering::Relaxed); + } + + pub fn record_hit(&self) { + self.cache_hits.fetch_add(1, Ordering::Relaxed); + } } impl NativeClassManager { @@ -70,6 +93,7 @@ impl NativeClassManager { cache, sender: None, compiler: None, + cache_metrics: Arc::new(CacheMetrics::new()), }; } @@ -82,6 +106,7 @@ impl NativeClassManager { cache, sender: None, compiler: Some(compiler), + cache_metrics: Arc::new(CacheMetrics::new()), }; } @@ -92,12 +117,28 @@ impl NativeClassManager { move || run_compilation_worker(cache, receiver, compiler) }); - NativeClassManager { cairo_native_run_config, cache, sender: Some(sender), compiler: None } + // TODO(AVIV): Add private constructor with default values. + NativeClassManager { + cairo_native_run_config, + cache, + sender: Some(sender), + compiler: None, + cache_metrics: Arc::new(CacheMetrics::new()), + } } /// Returns the runnable compiled class for the given class hash, if it exists in cache. pub fn get_runnable(&self, class_hash: &ClassHash) -> Option { - let cached_class = self.cache.get(class_hash)?; + let cached_class = match self.cache.get(class_hash) { + Some(class) => { + self.cache_metrics.record_hit(); + class + } + None => { + self.cache_metrics.record_miss(); + return None; + } + }; if let CachedClass::V1(_, _) = cached_class { // TODO(Yoni): make sure `wait_on_native_compilation` cannot be set to true while // `run_cairo_native` is false. @@ -195,6 +236,16 @@ impl NativeClassManager { pub fn get_cache_size(&self) -> usize { self.cache.lock().cache_size() } + + /// Retrieves the current cache miss counter and resets it to zero. + pub fn take_cache_miss_counter(&self) -> u64 { + self.cache_metrics.cache_misses.swap(0, Ordering::Relaxed) + } + + /// Retrieves the current cache hit counter and resets it to zero. + pub fn take_cache_hit_counter(&self) -> u64 { + self.cache_metrics.cache_hits.swap(0, Ordering::Relaxed) + } } /// Handles compilation requests from the channel, holding the receiver end of the channel. diff --git a/crates/blockifier/src/state/native_class_manager_test.rs b/crates/blockifier/src/state/native_class_manager_test.rs index f4a99a1568c..2d9355950b6 100644 --- a/crates/blockifier/src/state/native_class_manager_test.rs +++ b/crates/blockifier/src/state/native_class_manager_test.rs @@ -19,6 +19,7 @@ use crate::state::global_cache::{ }; use crate::state::native_class_manager::{ process_compilation_request, + CacheMetrics, CompilationRequest, ContractClassManagerError, NativeClassManager, @@ -157,6 +158,7 @@ fn test_send_compilation_request_channel_disconnected() { cache: RawClassCache::new(GLOBAL_CONTRACT_CACHE_SIZE_FOR_TEST), sender: Some(sender), compiler: None, + cache_metrics: Arc::new(CacheMetrics::new()), }; // Disconnect the channel by dropping the receiver. drop(receiver); diff --git a/crates/starknet_batcher/src/batcher.rs b/crates/starknet_batcher/src/batcher.rs index 29298b83fb7..69ee23b57cc 100644 --- a/crates/starknet_batcher/src/batcher.rs +++ b/crates/starknet_batcher/src/batcher.rs @@ -61,6 +61,8 @@ use crate::metrics::{ register_metrics, ProposalMetricsHandle, BATCHED_TRANSACTIONS, + CLASS_CACHE_HITS, + CLASS_CACHE_MISSES, REJECTED_TRANSACTIONS, REVERTED_BLOCKS, STORAGE_HEIGHT, @@ -496,6 +498,8 @@ impl Batcher { let execution_infos: Vec<_> = block_execution_artifacts.execution_infos.into_iter().map(|(_, info)| info).collect(); + CLASS_CACHE_MISSES.increment(self.block_builder_factory.take_class_cache_miss_counter()); + CLASS_CACHE_HITS.increment(self.block_builder_factory.take_class_cache_hit_counter()); BATCHED_TRANSACTIONS.increment(n_txs); REJECTED_TRANSACTIONS.increment(n_rejected_txs); diff --git a/crates/starknet_batcher/src/batcher_test.rs b/crates/starknet_batcher/src/batcher_test.rs index a44d55b1808..4e4bedf9d16 100644 --- a/crates/starknet_batcher/src/batcher_test.rs +++ b/crates/starknet_batcher/src/batcher_test.rs @@ -120,12 +120,16 @@ impl Default for MockDependencies { let expected_gas_price = propose_block_input(PROPOSAL_ID).block_info.gas_prices.strk_gas_prices.l2_gas_price; mempool_client.expect_update_gas_price().with(eq(expected_gas_price)).returning(|_| Ok(())); + let mut block_builder_factory = MockBlockBuilderFactoryTrait::new(); + block_builder_factory.expect_take_class_cache_miss_counter().return_const(0_u64); + block_builder_factory.expect_take_class_cache_hit_counter().return_const(0_u64); + Self { storage_reader, storage_writer: MockBatcherStorageWriterTrait::new(), l1_provider_client: MockL1ProviderClient::new(), mempool_client, - block_builder_factory: MockBlockBuilderFactoryTrait::new(), + block_builder_factory, // TODO(noamsp): use MockClassManagerClient class_manager_client: Arc::new(EmptyClassManagerClient), } diff --git a/crates/starknet_batcher/src/block_builder.rs b/crates/starknet_batcher/src/block_builder.rs index 90d0f228a01..54856ac358d 100644 --- a/crates/starknet_batcher/src/block_builder.rs +++ b/crates/starknet_batcher/src/block_builder.rs @@ -349,6 +349,10 @@ pub trait BlockBuilderFactoryTrait: Send + Sync { >, runtime: tokio::runtime::Handle, ) -> BlockBuilderResult<(Box, AbortSignalSender)>; + + fn take_class_cache_miss_counter(&self) -> u64; + + fn take_class_cache_hit_counter(&self) -> u64; } #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] @@ -466,6 +470,14 @@ impl BlockBuilderFactoryTrait for BlockBuilderFactory { )); Ok((block_builder, abort_signal_sender)) } + + fn take_class_cache_hit_counter(&self) -> u64 { + self.contract_class_manager.take_cache_hit_counter() + } + + fn take_class_cache_miss_counter(&self) -> u64 { + self.contract_class_manager.take_cache_miss_counter() + } } /// Supplementary information for use by downstream services. diff --git a/crates/starknet_batcher/src/metrics.rs b/crates/starknet_batcher/src/metrics.rs index 90a280762b9..fe3ac5e943a 100644 --- a/crates/starknet_batcher/src/metrics.rs +++ b/crates/starknet_batcher/src/metrics.rs @@ -8,6 +8,8 @@ define_metrics!( MetricGauge { STORAGE_HEIGHT, "batcher_storage_height", "The height of the batcher's storage" }, // Counters MetricCounter { PROPOSAL_STARTED, "batcher_proposal_started", "Counter of proposals started", init = 0 }, + MetricCounter { CLASS_CACHE_MISSES, "class_cache_misses", "Counter of global class cache misses", init=0 }, + MetricCounter { CLASS_CACHE_HITS, "class_cache_hits", "Counter of global class cache hits", init=0 }, MetricCounter { PROPOSAL_SUCCEEDED, "batcher_proposal_succeeded", "Counter of successful proposals", init = 0 }, MetricCounter { PROPOSAL_FAILED, "batcher_proposal_failed", "Counter of failed proposals", init = 0 }, MetricCounter { PROPOSAL_ABORTED, "batcher_proposal_aborted", "Counter of aborted proposals", init = 0 }, @@ -23,6 +25,8 @@ pub fn register_metrics(storage_height: BlockNumber) { STORAGE_HEIGHT.register(); #[allow(clippy::as_conversions)] STORAGE_HEIGHT.set(storage_height.0 as f64); + CLASS_CACHE_MISSES.register(); + CLASS_CACHE_HITS.register(); PROPOSAL_STARTED.register(); PROPOSAL_SUCCEEDED.register(); diff --git a/crates/starknet_sequencer_dashboard/Cargo.toml b/crates/starknet_sequencer_dashboard/Cargo.toml index 87605bdce9f..43e656b1a19 100644 --- a/crates/starknet_sequencer_dashboard/Cargo.toml +++ b/crates/starknet_sequencer_dashboard/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true workspace = true [dependencies] +const_format.workspace = true indexmap = { workspace = true, features = ["serde"] } serde = { workspace = true, features = ["derive"] } serde_json.workspace = true diff --git a/crates/starknet_sequencer_dashboard/src/dashboard_definitions.rs b/crates/starknet_sequencer_dashboard/src/dashboard_definitions.rs index e22a014e388..d4d1018b188 100644 --- a/crates/starknet_sequencer_dashboard/src/dashboard_definitions.rs +++ b/crates/starknet_sequencer_dashboard/src/dashboard_definitions.rs @@ -1,5 +1,8 @@ +use const_format::formatcp; use starknet_batcher::metrics::{ BATCHED_TRANSACTIONS, + CLASS_CACHE_HITS, + CLASS_CACHE_MISSES, PROPOSAL_FAILED, PROPOSAL_STARTED, PROPOSAL_SUCCEEDED, @@ -18,7 +21,6 @@ use starknet_sequencer_metrics::metric_definitions::{ }; use crate::dashboard::{Dashboard, Panel, PanelType, Row}; - #[cfg(test)] #[path = "dashboard_definitions_test.rs"] mod dashboard_definitions_test; @@ -57,6 +59,18 @@ const PANEL_BATCHED_TRANSACTIONS: Panel = Panel::new( PanelType::Stat, ); +const PANEL_CAIRO_NATIVE_CACHE_MISS_RATIO: Panel = Panel::new( + "cairo_native_cache_miss_ratio", + "The ratio of cache misses in the Cairo native cache", + formatcp!( + "100 * ({} / clamp_min(({} + {}), 1))", + CLASS_CACHE_MISSES.get_name(), + CLASS_CACHE_MISSES.get_name(), + CLASS_CACHE_HITS.get_name() + ), + PanelType::Graph, +); + const PANEL_MEMPOOL_P2P_NUM_CONNECTED_PEERS: Panel = Panel::new( MEMPOOL_P2P_NUM_CONNECTED_PEERS.get_name(), MEMPOOL_P2P_NUM_CONNECTED_PEERS.get_description(), @@ -158,6 +172,7 @@ const BATCHER_ROW: Row<'_> = Row::new( PANEL_PROPOSAL_SUCCEEDED, PANEL_PROPOSAL_FAILED, PANEL_BATCHED_TRANSACTIONS, + PANEL_CAIRO_NATIVE_CACHE_MISS_RATIO, ], ); const HTTP_SERVER_ROW: Row<'_> = Row::new(