Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cairo_native): add synchronous sierra to native compilation flow #2771

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 73 additions & 30 deletions crates/blockifier/src/state/contract_class_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::sync::Arc;
#[cfg(any(feature = "testing", test))]
use cached::Cached;
#[cfg(feature = "cairo_native")]
use log::{error, info};
use log;
use starknet_api::core::ClassHash;
#[cfg(feature = "cairo_native")]
use starknet_api::state::SierraContractClass;
Expand Down Expand Up @@ -52,13 +52,19 @@ pub struct ContractClassManager {
/// disabled.
#[cfg(feature = "cairo_native")]
sender: Option<SyncSender<CompilationRequest>>,
/// The sierra-to-native compiler.
#[cfg(feature = "cairo_native")]
compiler: Option<Arc<dyn SierraToNativeCompiler>>,
}

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.
/// NOTE: If native compilation is disabled, the compilation worker is not spawned.
/// NOTE: the compilation worker is not spawned if one of the following conditions is met:
/// 1. The feature `cairo_native` is not enabled.
/// 2. `config.run_cairo_native` is `false`.
/// 3. `config.wait_on_native_compilation` is `true`.
pub fn start(config: ContractClassManagerConfig) -> ContractClassManager {
// TODO(Avi, 15/12/2024): Add the size of the channel to the config.
let contract_caches = ContractCaches::new(config.contract_cache_size);
Expand All @@ -68,35 +74,61 @@ impl ContractClassManager {
{
if !config.run_cairo_native {
// Native compilation is disabled - no need to start the compilation worker.
return ContractClassManager { config, contract_caches, sender: None };
return ContractClassManager {
config,
contract_caches,
sender: None,
compiler: None,
};
}

let compiler_config = SierraToCasmCompilationConfig::default();
let compiler = Arc::new(CommandLineCompiler::new(compiler_config));
if config.wait_on_native_compilation {
// Compilation requests are processed synchronously. No need to start the worker.
return ContractClassManager {
config,
contract_caches,
sender: None,
compiler: Some(compiler),
};
}

let (sender, receiver) = sync_channel(CHANNEL_SIZE);

std::thread::spawn({
let contract_caches = contract_caches.clone();
let compiler_config = SierraToCasmCompilationConfig::default();
let compiler = CommandLineCompiler::new(compiler_config);

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

ContractClassManager { config, contract_caches, sender: Some(sender) }
ContractClassManager { config, contract_caches, sender: Some(sender), compiler: None }
}
}

/// Sends a compilation request to the compilation worker. Does not block the sender. Logs an
/// error if the channel is full.
/// Sends a compilation request. Two cases:
/// 1. If `config.wait_on_native_compilation` is `false`, sends the request to the compilation
/// worker. Does not block the sender. Logs an error if the channel is full.
/// 2. Otherwise, processes the request synchronously, blocking the sender until the request is
/// processed.
#[cfg(feature = "cairo_native")]
pub fn send_compilation_request(&self, request: CompilationRequest) {
assert!(self.config.run_cairo_native, "Native compilation is disabled.");
if self.config.wait_on_native_compilation {
// Compilation requests are processed synchronously. No need to go through the channel.
let compiler = self.compiler.as_ref().expect("Compiler not available.");
process_compilation_request(self.contract_caches.clone(), compiler.clone(), request);
return;
}

let sender = self.sender.as_ref().expect("Compilation channel not available.");
// TODO(Avi, 15/12/2024): Check for duplicated requests.
sender.try_send(request).unwrap_or_else(|err| match err {
TrySendError::Full((class_hash, _, _)) => {
error!(
log::error!(
"Compilation request channel is full (size: {}). Compilation request for \
class hash {} was not sent.",
CHANNEL_SIZE, class_hash
CHANNEL_SIZE,
class_hash
)
}
TrySendError::Disconnected(_) => {
Expand Down Expand Up @@ -139,27 +171,38 @@ impl ContractClassManager {
fn run_compilation_worker(
contract_caches: ContractCaches,
receiver: Receiver<CompilationRequest>,
compiler: impl SierraToNativeCompiler,
compiler: Arc<dyn SierraToNativeCompiler>,
) {
log::info!("Compilation worker started.");
for compilation_request in receiver.iter() {
process_compilation_request(contract_caches.clone(), compiler.clone(), compilation_request);
}
log::info!("Compilation worker terminated.");
}

/// Processes a compilation request and caches the compiled class in the contract caches.
#[cfg(feature = "cairo_native")]
fn process_compilation_request(
contract_caches: ContractCaches,
compiler: Arc<dyn SierraToNativeCompiler>,
compilation_request: CompilationRequest,
) {
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 (class_hash, sierra, casm) = compilation_request;
if contract_caches.get_native(&class_hash).is_some() {
// The contract class is already compiled to native - skip the compilation.
return;
}
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));
}
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);
}
Err(err) => {
log::error!("Error compiling contract class: {}", err);
contract_caches.set_native(class_hash, CachedCairoNative::CompilationFailed);
}
}
info!("Compilation worker terminated.");
}
Loading