Skip to main content

sp1_core_executor/
opts.rs

1use crate::{
2    cost_and_height_per_syscall, rv64im_costs, utils::trunc_32, RetainedEventsPreset, RiscvAirId,
3    SyscallCode, BYTE_NUM_ROWS, RANGE_NUM_ROWS,
4};
5use enum_map::EnumMap;
6use serde::{Deserialize, Serialize};
7use std::{collections::HashSet, env};
8
9const MAX_SHARD_SIZE: usize = 1 << 24;
10
11/// The trace area threshold for a shard.
12pub const ELEMENT_THRESHOLD: u64 = (1 << 28) + (1 << 27);
13/// The height threshold for a shard.
14pub const HEIGHT_THRESHOLD: u64 = 1 << 22;
15/// The maximum size of a minimal trace chunk in terms of memory entries.
16pub const MINIMAL_TRACE_CHUNK_THRESHOLD: u64 =
17    2147483648 / std::mem::size_of::<sp1_jit::MemValue>() as u64;
18/// The default number trace chunk slots
19pub const DEFAULT_TRACE_CHUNK_SLOTS: usize = 5;
20/// Default memory limit for SP1 programs, note this value has different semantics
21/// on different implementation. For native executor, it is the limit on total
22/// process memory(resident set size, or RSS) of thie entire child process. For
23/// portable executor, it is merely the limit on created memory entries. This
24/// means the actual memory usage for portable executor will exceed this limit.
25pub const DEFAULT_MEMORY_LIMIT: u64 = 24 * 1024 * 1024 * 1024;
26
27/// The threshold that determines when to split the shard.
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
29pub struct ShardingThreshold {
30    /// The maximum number of elements in the trace.
31    pub element_threshold: u64,
32    /// The maximum number of rows for a single operation.
33    pub height_threshold: u64,
34}
35
36/// Options for the core prover.
37#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
38pub struct SP1CoreOpts {
39    /// The maximum size of a minimal trace chunk in terms of memory entries.
40    pub minimal_trace_chunk_threshold: u64,
41    /// The number of slots in trace chunk ring buffer.
42    pub trace_chunk_slots: usize,
43    /// The memory limit of SP1 program.
44    pub memory_limit: u64,
45    /// The size of a shard in terms of cycles. Used for estimating event counts when allocating records.
46    pub shard_size: usize,
47    /// The threshold that determines when to split the shard.
48    pub sharding_threshold: ShardingThreshold,
49    /// Preset collections of events to retain in a shard instead of deferring.
50    pub retained_events_presets: HashSet<RetainedEventsPreset>,
51    /// Use optimized `generate_dependencies` for global chip.
52    pub global_dependencies_opt: bool,
53}
54
55impl Default for SP1CoreOpts {
56    fn default() -> Self {
57        let minimal_trace_chunk_threshold = env::var("MINIMAL_TRACE_CHUNK_THRESHOLD").map_or_else(
58            |_| MINIMAL_TRACE_CHUNK_THRESHOLD,
59            |s| s.parse::<u64>().unwrap_or(MINIMAL_TRACE_CHUNK_THRESHOLD),
60        );
61
62        let trace_chunk_slots = env::var("TRACE_CHUNK_SLOTS").map_or_else(
63            |_| DEFAULT_TRACE_CHUNK_SLOTS,
64            |s| s.parse::<usize>().unwrap_or(DEFAULT_TRACE_CHUNK_SLOTS),
65        );
66
67        let memory_limit = env::var("MEMORY_LIMIT").map_or_else(
68            |_| DEFAULT_MEMORY_LIMIT,
69            |s| s.parse::<u64>().unwrap_or(DEFAULT_MEMORY_LIMIT),
70        );
71
72        let shard_size = env::var("SHARD_SIZE")
73            .map_or_else(|_| MAX_SHARD_SIZE, |s| s.parse::<usize>().unwrap_or(MAX_SHARD_SIZE));
74
75        let element_threshold = env::var("ELEMENT_THRESHOLD")
76            .map_or_else(|_| ELEMENT_THRESHOLD, |s| s.parse::<u64>().unwrap_or(ELEMENT_THRESHOLD));
77
78        let height_threshold = env::var("HEIGHT_THRESHOLD")
79            .map_or_else(|_| HEIGHT_THRESHOLD, |s| s.parse::<u64>().unwrap_or(HEIGHT_THRESHOLD));
80
81        let sharding_threshold = ShardingThreshold { element_threshold, height_threshold };
82
83        let mut retained_events_presets = HashSet::new();
84        retained_events_presets.insert(RetainedEventsPreset::Bls12381Field);
85        retained_events_presets.insert(RetainedEventsPreset::Bn254Field);
86        retained_events_presets.insert(RetainedEventsPreset::Sha256);
87        retained_events_presets.insert(RetainedEventsPreset::Poseidon2);
88        retained_events_presets.insert(RetainedEventsPreset::U256Ops);
89
90        Self {
91            minimal_trace_chunk_threshold,
92            trace_chunk_slots,
93            memory_limit,
94            shard_size,
95            sharding_threshold,
96            retained_events_presets,
97            global_dependencies_opt: false,
98        }
99    }
100}
101
102/// Options for splitting deferred events.
103#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
104pub struct SplitOpts {
105    /// The threshold for combining the memory and page prot init/finalize events in to the current
106    /// shard in terms of the estimated trace area of the shard.
107    pub pack_trace_threshold: u64,
108    /// The threshold for combining the memory init/finalize events in to the current shard in
109    /// terms of the number of memory init/finalize events.
110    pub combine_memory_threshold: usize,
111    /// The threshold for combining the page prot init/finalize events in to the current shard in
112    /// terms of the number of page prot init/finalize events.
113    pub combine_page_prot_threshold: usize,
114    /// The threshold for syscall codes.
115    pub syscall_threshold: EnumMap<SyscallCode, usize>,
116    /// The threshold for memory events.
117    pub memory: usize,
118    /// The threshold for page prot events.
119    pub page_prot: usize,
120}
121
122impl SplitOpts {
123    /// Create a new [`SplitOpts`] with the given [`SP1CoreOpts`] and the program size.
124    #[must_use]
125    pub fn new(opts: &SP1CoreOpts, program_size: usize, page_protect_allowed: bool) -> Self {
126        assert!(!page_protect_allowed, "page protection is turned off");
127
128        let costs = rv64im_costs();
129
130        let mut available_trace_area = opts.sharding_threshold.element_threshold;
131        let mut fixed_trace_area = 0;
132        fixed_trace_area += program_size.next_multiple_of(32) * costs[&RiscvAirId::Program];
133        fixed_trace_area += BYTE_NUM_ROWS as usize * costs[&RiscvAirId::Byte];
134        fixed_trace_area += RANGE_NUM_ROWS as usize * costs[&RiscvAirId::Range];
135
136        assert!(
137            available_trace_area >= fixed_trace_area as u64,
138            "SP1CoreOpts's element threshold is too low"
139        );
140
141        available_trace_area -= fixed_trace_area as u64;
142
143        let max_height = opts.sharding_threshold.height_threshold;
144
145        let syscall_threshold = EnumMap::from_fn(|syscall_code: SyscallCode| {
146            if syscall_code.should_send() == 0 || syscall_code.as_air_id().is_none() {
147                return 0;
148            }
149
150            let (cost_per_syscall, max_height_per_syscall) =
151                cost_and_height_per_syscall(syscall_code, &costs, page_protect_allowed);
152            let element_threshold = trunc_32(available_trace_area as usize / cost_per_syscall);
153            let height_threshold = trunc_32(max_height as usize / max_height_per_syscall);
154
155            element_threshold.min(height_threshold)
156        });
157
158        let cost_per_memory = costs[&RiscvAirId::MemoryGlobalInit] + costs[&RiscvAirId::Global];
159        let memory = trunc_32(
160            (available_trace_area as usize / cost_per_memory).min(max_height as usize) / 2,
161        );
162
163        // Allocate `2/3` of the trace area to the usual trace area.
164        let pack_trace_threshold = 2 * opts.sharding_threshold.element_threshold / 3;
165        // Allocate `3/10` of the trace area to `MemoryGlobal`.
166        let combine_memory_threshold =
167            trunc_32(3 * opts.sharding_threshold.element_threshold as usize / cost_per_memory / 20);
168
169        Self {
170            pack_trace_threshold,
171            combine_memory_threshold,
172            combine_page_prot_threshold: 0,
173            syscall_threshold,
174            memory,
175            page_prot: 0,
176        }
177    }
178}