Skip to content

Commit

Permalink
feat(cairo_native): add batcher compiler struct
Browse files Browse the repository at this point in the history
  • Loading branch information
avi-starkware committed Nov 28, 2024
1 parent be65c40 commit 3f7ad1e
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 15 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion crates/blockifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ description = "The transaction-executing component in the Starknet sequencer."
workspace = true

[features]
cairo_native = ["dep:cairo-native"]
cairo_native = ["dep:cairo-native", "starknet_sierra_compile/cairo_native"]
jemalloc = ["dep:tikv-jemallocator"]
reexecution = ["transaction_serde"]
testing = ["rand", "rstest", "starknet_api/testing"]
Expand Down Expand Up @@ -50,6 +50,7 @@ serde_json = { workspace = true, features = ["arbitrary_precision"] }
sha2.workspace = true
starknet-types-core.workspace = true
starknet_api.workspace = true
starknet_sierra_compile = { workspace = true, optional = true }
strum.workspace = true
strum_macros.workspace = true
tempfile.workspace = true
Expand Down
2 changes: 2 additions & 0 deletions crates/blockifier/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
pub mod cached_state;
#[cfg(feature = "cairo_native")]
pub mod contract_class_manager;
#[cfg(test)]
pub mod error_format_test;
pub mod errors;
Expand Down
129 changes: 129 additions & 0 deletions crates/blockifier/src/state/contract_class_manager.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
use std::sync::mpsc::{sync_channel, Receiver, SyncSender, TrySendError};
use std::sync::Arc;

use log::{error, info};
use starknet_api::core::ClassHash;
use starknet_api::state::SierraContractClass;
use starknet_sierra_compile::command_line_compiler::CommandLineCompiler;
use starknet_sierra_compile::config::SierraToCasmCompilationConfig;
use starknet_sierra_compile::utils::into_contract_class_for_compilation;
use starknet_sierra_compile::SierraToNativeCompiler;

use crate::execution::contract_class::{CompiledClassV1, RunnableCompiledClass};
use crate::execution::native::contract_class::NativeCompiledClassV1;
use crate::state::global_cache::{CachedCairoNative, ContractCaches};

const CHANNEL_SIZE: usize = 1000;

/// Represents a request to compile a sierra contract class to a native compiled class.
///
/// # Fields:
/// * `class_hash` - used to identify the contract class in the cache.
/// * `sierra_contract_class` - the sierra contract class to be compiled.
/// * `casm_compiled_class` - stored in [`NativeCompiledClassV1`] to allow fallback to cairo_vm
/// execution in case of unxecpected failure during native execution.
type CompilationRequest = (ClassHash, Arc<SierraContractClass>, CompiledClassV1);

/// Manages the global cache of contract classes and handles sierra-to-native compilation requests.
struct ContractClassManager {
// The global cache of contract classes: casm, sierra, and native.
contract_caches: Arc<ContractCaches>,
// The sending half of the compilation request channel.
sender: SyncSender<CompilationRequest>,
}

#[allow(dead_code)]
impl ContractClassManager {
/// Creates a new contract class manager and spawns a thread that listens for compilation
/// requests and processes them (a.k.a. the compilation worker).
/// Returns the contract class manager.
pub fn start(contract_caches: ContractCaches) -> ContractClassManager {
// TODO(Avi, 15/12/2024): Add the size of the channel to the config.
let contract_caches = Arc::new(contract_caches);
let (sender, receiver) = sync_channel(CHANNEL_SIZE);
let compiler_config = SierraToCasmCompilationConfig::default();
let compiler = CommandLineCompiler::new(compiler_config);

std::thread::spawn({
let contract_caches = Arc::clone(&contract_caches);
let compiler = Arc::new(compiler);

move || run_compilation_worker(contract_caches, receiver, compiler)
});

ContractClassManager { contract_caches, sender }
}

/// Sends a compilation request to the compilation worker. Does not block the sender. Logs an
/// error is the channel is full.
pub fn send_compilation_request(&self, request: CompilationRequest) {
self.cache_request_contracts(&request);
// TODO(Avi, 15/12/2024): Check for duplicated requests.
self.sender.try_send(request).unwrap_or_else(|err| match err {
TrySendError::Full((class_hash, _, _)) => {
error!(
"Compilation request channel is full (size: {}). Compilation request for \
class hash {} was not sent.",
CHANNEL_SIZE, class_hash
)
}
TrySendError::Disconnected(_) => {
panic!("Compilation request channel is closed.")
}
});
}

/// Returns the native compiled class for the given class hash, if it exists in cache.
pub fn get_native(&self, class_hash: &ClassHash) -> Option<CachedCairoNative> {
self.contract_caches.get_native(class_hash)
}

/// Returns the Sierra contract class for the given class hash, if it exists in cache.
pub fn get_sierra(&self, class_hash: &ClassHash) -> Option<Arc<SierraContractClass>> {
self.contract_caches.get_sierra(class_hash)
}

/// Returns the casm compiled class for the given class hash, if it exists in cache.
pub fn get_casm(&self, class_hash: &ClassHash) -> Option<RunnableCompiledClass> {
self.contract_caches.get_casm(class_hash)
}

/// Caches the sierra and casm contract classes of a compilation request.
fn cache_request_contracts(&self, request: &CompilationRequest) {
let (class_hash, sierra, casm) = request.clone();
self.contract_caches.set_sierra(class_hash, sierra);
let cached_casm = RunnableCompiledClass::from(casm);
self.contract_caches.set_casm(class_hash, cached_casm);
}
}

/// Handles compilation requests from the channel, holding the receiver end of the channel.
/// If no request is available, non-busy-waits until a request is available.
/// When the sender is dropped, the worker processes all pending requests and terminates.
fn run_compilation_worker(
contract_caches: Arc<ContractCaches>,
receiver: Receiver<CompilationRequest>,
compiler: Arc<dyn SierraToNativeCompiler>,
) {
info!("Compilation worker started.");
for (class_hash, sierra, casm) in receiver.iter() {
if contract_caches.get_native(&class_hash).is_some() {
// The contract class is already compiled to native - skip the compilation.
continue;
}
let sierra_for_compilation = into_contract_class_for_compilation(sierra.as_ref());
let compilation_result = compiler.compile_to_native(sierra_for_compilation);
match compilation_result {
Ok(executor) => {
let native_compiled_class = NativeCompiledClassV1::new(executor, casm);
contract_caches
.set_native(class_hash, CachedCairoNative::Compiled(native_compiled_class));
}
Err(err) => {
error!("Error compiling contract class: {}", err);
contract_caches.set_native(class_hash, CachedCairoNative::CompilationFailed);
}
}
}
info!("Compilation worker terminated.");
}
28 changes: 14 additions & 14 deletions crates/blockifier/src/state/global_cache.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
use std::sync::{Arc, Mutex, MutexGuard};

use cached::{Cached, SizedCache};
#[cfg(feature = "cairo_native")]
use cairo_native::executor::AotContractExecutor;
use starknet_api::core::ClassHash;
#[cfg(feature = "cairo_native")]
use starknet_api::state::SierraContractClass;

#[cfg(feature = "cairo_native")]
use crate::execution::contract_class::RunnableCompiledClass;
#[cfg(feature = "cairo_native")]
use crate::execution::native::contract_class::NativeCompiledClassV1;

type ContractClassLRUCache<T> = SizedCache<ClassHash, T>;
pub type LockedContractClassCache<'a, T> = MutexGuard<'a, ContractClassLRUCache<T>>;
type ContractLRUCache<T> = SizedCache<ClassHash, T>;
pub type LockedClassCache<'a, T> = MutexGuard<'a, ContractLRUCache<T>>;
#[derive(Debug, Clone)]
// Thread-safe LRU cache for contract classes, optimized for inter-language sharing when
// `blockifier` compiles as a shared library.
// Thread-safe LRU cache for contract classes (Seirra or compiled Casm/Native), optimized for
// inter-language sharing when `blockifier` compiles as a shared library.
// TODO(Yoni, 1/1/2025): consider defining CachedStateReader.
pub struct GlobalContractCache<T: Clone>(pub Arc<Mutex<ContractClassLRUCache<T>>>);
pub struct GlobalContractCache<T: Clone>(pub Arc<Mutex<ContractLRUCache<T>>>);

#[cfg(feature = "cairo_native")]
#[derive(Debug, Clone)]
pub enum CachedCairoNative {
Compiled(AotContractExecutor),
Compiled(NativeCompiledClassV1),
CompilationFailed,
}

Expand All @@ -30,7 +30,7 @@ pub const GLOBAL_CONTRACT_CACHE_SIZE_FOR_TEST: usize = 400;
impl<T: Clone> GlobalContractCache<T> {
/// Locks the cache for atomic access. Although conceptually shared, writing to this cache is
/// only possible for one writer at a time.
pub fn lock(&self) -> LockedContractClassCache<'_, T> {
pub fn lock(&self) -> LockedClassCache<'_, T> {
self.0.lock().expect("Global contract cache is poisoned.")
}

Expand All @@ -47,25 +47,25 @@ impl<T: Clone> GlobalContractCache<T> {
}

pub fn new(cache_size: usize) -> Self {
Self(Arc::new(Mutex::new(ContractClassLRUCache::<T>::with_size(cache_size))))
Self(Arc::new(Mutex::new(ContractLRUCache::<T>::with_size(cache_size))))
}
}

#[cfg(feature = "cairo_native")]
pub struct GlobalContractCacheManager {
pub struct ContractCaches {
pub casm_cache: GlobalContractCache<RunnableCompiledClass>,
pub native_cache: GlobalContractCache<CachedCairoNative>,
pub sierra_cache: GlobalContractCache<Arc<SierraContractClass>>,
}

#[cfg(feature = "cairo_native")]
impl GlobalContractCacheManager {
impl ContractCaches {
pub fn get_casm(&self, class_hash: &ClassHash) -> Option<RunnableCompiledClass> {
self.casm_cache.get(class_hash)
}

pub fn set_casm(&self, class_hash: ClassHash, contract_class: RunnableCompiledClass) {
self.casm_cache.set(class_hash, contract_class);
pub fn set_casm(&self, class_hash: ClassHash, compiled_class: RunnableCompiledClass) {
self.casm_cache.set(class_hash, compiled_class);
}

pub fn get_native(&self, class_hash: &ClassHash) -> Option<CachedCairoNative> {
Expand Down

0 comments on commit 3f7ad1e

Please sign in to comment.