Skip to main content

Basics

In this section, we'll cover some of the basics of optimizing the performance of your SP1 programs, with a focus on improving the end to end costs.

Program Optimization

  1. The first step to optimizing your program is to identify the performance bottlenecks. You can do this by profiling your application or using cycle tracking.
  2. Compiling your program with different settings can yield better performance characteristics.
    • Set lto to thin, true, or fat to enable link-time optimization. This can yield significant performance improvements.
    • Set codegen-units to 1 to disable parallel codegen.
  3. Avoid copying or de(serializing) data unnecessarily.

Cryptographic Acceleration

If your program makes heavy use of cryptographic primitives (SHA-256, Keccak, etc.), SP1 supports accelerated "precompiles" for these operations.

Follow the instructions here to use them.

Prover Acceleration

SP1 supports both GPU (with CUDA) and CPU (with AVX on Intel and ARM) hardware acceleration for proving.

I/O Optimizations

If your program makes heavy use of (de)serializing data, you can make use of the following techniques to optimize performance of input reading and output writing.

Zero-Copy (De)serialization

Rather than using write and read to serialize and deserialize data in the zkVM context (which invokes bincode serialization by default), you can use write_vec and read_slice to serialize and deserialize data in the zkVM context.

bincode is a popular serialization library for Rust, but it is not optimized for the zkVM context and can be CPU intensive. Generally, we recommend using write_vec and read_slice in combination with zero-copy deserialization libraries like rkyv.

For rkyv, you can use the #[derive(Archive, Serialize, Deserialize)] macro to automatically derive the Archive, Serialize, and Deserialize traits for your structs.

use rkyv::{Archive, Serialize, Deserialize};

#[derive(Archive, Serialize, Deserialize)]
struct MyStruct {
field: u64,
}

You can then use rkyv::to_bytes to serialize your structs in your script and rkyv::from_bytes to deserialize them in your program.

use rkyv::rancor::Error;

let mut stdin = SP1Stdin::new();
let my_struct = MyStruct { field: 42 };
let bytes = rkyv::to_bytes::<Error>(&my_struct);

stdin.write_slice(&bytes);
use rkyv::rancor::Error;

let input = sp1_zkvm::io::read_vec();
let deserialized_struct = rkyv::from_bytes::<MyStruct, Error>(&input).unwrap();

Alternative Serialization Libraries

If rkyv is difficult to integrate with your codebase, you can benchmark alternative serialization libraries or derive your own de(serialization) logic for better I/O performance.