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
19/// The threshold that determines when to split the shard.
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
21pub struct ShardingThreshold {
22    /// The maximum number of elements in the trace.
23    pub element_threshold: u64,
24    /// The maximum number of rows for a single operation.
25    pub height_threshold: u64,
26}
27
28/// Options for the core prover.
29#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
30pub struct SP1CoreOpts {
31    /// The maximum size of a minimal trace chunk in terms of memory entries.
32    pub minimal_trace_chunk_threshold: u64,
33    /// The size of a shard in terms of cycles. Used for estimating event counts when allocating records.
34    pub shard_size: usize,
35    /// The threshold that determines when to split the shard.
36    pub sharding_threshold: ShardingThreshold,
37    /// Preset collections of events to retain in a shard instead of deferring.
38    pub retained_events_presets: HashSet<RetainedEventsPreset>,
39    /// Use optimized `generate_dependencies` for global chip.
40    pub global_dependencies_opt: bool,
41}
42
43impl Default for SP1CoreOpts {
44    fn default() -> Self {
45        let minimal_trace_chunk_threshold = env::var("MINIMAL_TRACE_CHUNK_THRESHOLD").map_or_else(
46            |_| MINIMAL_TRACE_CHUNK_THRESHOLD,
47            |s| s.parse::<u64>().unwrap_or(MINIMAL_TRACE_CHUNK_THRESHOLD),
48        );
49
50        let shard_size = env::var("SHARD_SIZE")
51            .map_or_else(|_| MAX_SHARD_SIZE, |s| s.parse::<usize>().unwrap_or(MAX_SHARD_SIZE));
52
53        let element_threshold = env::var("ELEMENT_THRESHOLD")
54            .map_or_else(|_| ELEMENT_THRESHOLD, |s| s.parse::<u64>().unwrap_or(ELEMENT_THRESHOLD));
55
56        let height_threshold = env::var("HEIGHT_THRESHOLD")
57            .map_or_else(|_| HEIGHT_THRESHOLD, |s| s.parse::<u64>().unwrap_or(HEIGHT_THRESHOLD));
58
59        let sharding_threshold = ShardingThreshold { element_threshold, height_threshold };
60
61        let mut retained_events_presets = HashSet::new();
62        retained_events_presets.insert(RetainedEventsPreset::Bls12381Field);
63        retained_events_presets.insert(RetainedEventsPreset::Bn254Field);
64        retained_events_presets.insert(RetainedEventsPreset::Sha256);
65        retained_events_presets.insert(RetainedEventsPreset::Poseidon2);
66        retained_events_presets.insert(RetainedEventsPreset::U256Ops);
67
68        Self {
69            minimal_trace_chunk_threshold,
70            shard_size,
71            sharding_threshold,
72            retained_events_presets,
73            global_dependencies_opt: false,
74        }
75    }
76}
77
78/// Options for splitting deferred events.
79#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
80pub struct SplitOpts {
81    /// The threshold for combining the memory and page prot init/finalize events in to the current
82    /// shard in terms of the estimated trace area of the shard.
83    pub pack_trace_threshold: u64,
84    /// The threshold for combining the memory init/finalize events in to the current shard in
85    /// terms of the number of memory init/finalize events.
86    pub combine_memory_threshold: usize,
87    /// The threshold for combining the page prot init/finalize events in to the current shard in
88    /// terms of the number of page prot init/finalize events.
89    pub combine_page_prot_threshold: usize,
90    /// The threshold for syscall codes.
91    pub syscall_threshold: EnumMap<SyscallCode, usize>,
92    /// The threshold for memory events.
93    pub memory: usize,
94    /// The threshold for page prot events.
95    pub page_prot: usize,
96}
97
98impl SplitOpts {
99    /// Create a new [`SplitOpts`] with the given [`SP1CoreOpts`] and the program size.
100    #[must_use]
101    pub fn new(opts: &SP1CoreOpts, program_size: usize, page_protect_allowed: bool) -> Self {
102        assert!(!page_protect_allowed, "page protection is turned off");
103
104        let costs = rv64im_costs();
105
106        let mut available_trace_area = opts.sharding_threshold.element_threshold;
107        let mut fixed_trace_area = 0;
108        fixed_trace_area += program_size.next_multiple_of(32) * costs[&RiscvAirId::Program];
109        fixed_trace_area += BYTE_NUM_ROWS as usize * costs[&RiscvAirId::Byte];
110        fixed_trace_area += RANGE_NUM_ROWS as usize * costs[&RiscvAirId::Range];
111
112        assert!(
113            available_trace_area >= fixed_trace_area as u64,
114            "SP1CoreOpts's element threshold is too low"
115        );
116
117        available_trace_area -= fixed_trace_area as u64;
118
119        let max_height = opts.sharding_threshold.height_threshold;
120
121        let syscall_threshold = EnumMap::from_fn(|syscall_code: SyscallCode| {
122            if syscall_code.should_send() == 0 {
123                return 0;
124            }
125
126            let (cost_per_syscall, max_height_per_syscall) =
127                cost_and_height_per_syscall(syscall_code, &costs, page_protect_allowed);
128            let element_threshold = trunc_32(available_trace_area as usize / cost_per_syscall);
129            let height_threshold = trunc_32(max_height as usize / max_height_per_syscall);
130
131            element_threshold.min(height_threshold)
132        });
133
134        let cost_per_memory = costs[&RiscvAirId::MemoryGlobalInit] + costs[&RiscvAirId::Global];
135        let memory = trunc_32(
136            (available_trace_area as usize / cost_per_memory).min(max_height as usize) / 2,
137        );
138
139        // Allocate `2/3` of the trace area to the usual trace area.
140        let pack_trace_threshold = 2 * opts.sharding_threshold.element_threshold / 3;
141        // Allocate `3/10` of the trace area to `MemoryGlobal`.
142        let combine_memory_threshold =
143            trunc_32(3 * opts.sharding_threshold.element_threshold as usize / cost_per_memory / 20);
144
145        Self {
146            pack_trace_threshold,
147            combine_memory_threshold,
148            combine_page_prot_threshold: 0,
149            syscall_threshold,
150            memory,
151            page_prot: 0,
152        }
153    }
154}