sp1_core_executor/
opts.rs1use 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
11pub const ELEMENT_THRESHOLD: u64 = (1 << 28) + (1 << 27);
13pub const HEIGHT_THRESHOLD: u64 = 1 << 22;
15pub const MINIMAL_TRACE_CHUNK_THRESHOLD: u64 = 16_777_216;
21pub const DEFAULT_TRACE_CHUNK_SLOTS: usize = 5;
23pub const DEFAULT_MEMORY_LIMIT: u64 = 24 * 1024 * 1024 * 1024;
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
32pub struct ShardingThreshold {
33 pub element_threshold: u64,
35 pub height_threshold: u64,
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
41pub struct SP1CoreOpts {
42 pub minimal_trace_chunk_threshold: u64,
44 pub trace_chunk_slots: usize,
46 pub memory_limit: u64,
48 pub shard_size: usize,
50 pub sharding_threshold: ShardingThreshold,
52 pub retained_events_presets: HashSet<RetainedEventsPreset>,
54 pub global_dependencies_opt: bool,
56}
57
58impl Default for SP1CoreOpts {
59 fn default() -> Self {
60 let minimal_trace_chunk_threshold = env::var("MINIMAL_TRACE_CHUNK_THRESHOLD").map_or_else(
61 |_| MINIMAL_TRACE_CHUNK_THRESHOLD,
62 |s| s.parse::<u64>().unwrap_or(MINIMAL_TRACE_CHUNK_THRESHOLD),
63 );
64
65 let trace_chunk_slots = env::var("TRACE_CHUNK_SLOTS").map_or_else(
66 |_| DEFAULT_TRACE_CHUNK_SLOTS,
67 |s| s.parse::<usize>().unwrap_or(DEFAULT_TRACE_CHUNK_SLOTS),
68 );
69
70 let memory_limit = env::var("MEMORY_LIMIT").map_or_else(
71 |_| DEFAULT_MEMORY_LIMIT,
72 |s| s.parse::<u64>().unwrap_or(DEFAULT_MEMORY_LIMIT),
73 );
74
75 let shard_size = env::var("SHARD_SIZE")
76 .map_or_else(|_| MAX_SHARD_SIZE, |s| s.parse::<usize>().unwrap_or(MAX_SHARD_SIZE));
77
78 let element_threshold = env::var("ELEMENT_THRESHOLD")
79 .map_or_else(|_| ELEMENT_THRESHOLD, |s| s.parse::<u64>().unwrap_or(ELEMENT_THRESHOLD));
80
81 let height_threshold = env::var("HEIGHT_THRESHOLD")
82 .map_or_else(|_| HEIGHT_THRESHOLD, |s| s.parse::<u64>().unwrap_or(HEIGHT_THRESHOLD));
83
84 let sharding_threshold = ShardingThreshold { element_threshold, height_threshold };
85
86 let mut retained_events_presets = HashSet::new();
87 retained_events_presets.insert(RetainedEventsPreset::Bls12381Field);
88 retained_events_presets.insert(RetainedEventsPreset::Bn254Field);
89 retained_events_presets.insert(RetainedEventsPreset::Sha256);
90 retained_events_presets.insert(RetainedEventsPreset::Poseidon2);
91 retained_events_presets.insert(RetainedEventsPreset::U256Ops);
92
93 Self {
94 minimal_trace_chunk_threshold,
95 trace_chunk_slots,
96 memory_limit,
97 shard_size,
98 sharding_threshold,
99 retained_events_presets,
100 global_dependencies_opt: false,
101 }
102 }
103}
104
105#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
107pub struct SplitOpts {
108 pub pack_trace_threshold: u64,
111 pub combine_memory_threshold: usize,
114 pub combine_page_prot_threshold: usize,
117 pub syscall_threshold: EnumMap<SyscallCode, usize>,
119 pub memory: usize,
121 pub page_prot: usize,
123}
124
125impl SplitOpts {
126 #[must_use]
128 pub fn new(opts: &SP1CoreOpts, program_size: usize, page_protect_allowed: bool) -> Self {
129 let costs = rv64im_costs();
130
131 let mut available_trace_area = opts.sharding_threshold.element_threshold;
132 let mut fixed_trace_area = 0;
133 fixed_trace_area += program_size.next_multiple_of(32) * costs[&RiscvAirId::Program];
134 fixed_trace_area += BYTE_NUM_ROWS as usize * costs[&RiscvAirId::Byte];
135 fixed_trace_area += RANGE_NUM_ROWS as usize * costs[&RiscvAirId::Range];
136
137 assert!(
138 available_trace_area >= fixed_trace_area as u64,
139 "SP1CoreOpts's element threshold is too low"
140 );
141
142 available_trace_area -= fixed_trace_area as u64;
143
144 let max_height = opts.sharding_threshold.height_threshold;
145
146 let syscall_threshold = EnumMap::from_fn(|syscall_code: SyscallCode| {
147 if syscall_code.should_send() == 0 || syscall_code.as_air_id().is_none() {
148 return 0;
149 }
150
151 let (cost_per_syscall, max_height_per_syscall) =
152 cost_and_height_per_syscall(syscall_code, &costs, page_protect_allowed);
153 let element_threshold = trunc_32(available_trace_area as usize / cost_per_syscall);
154 let height_threshold = trunc_32(max_height as usize / max_height_per_syscall);
155
156 element_threshold.min(height_threshold)
157 });
158
159 let cost_per_memory = costs[&RiscvAirId::MemoryGlobalInit] + costs[&RiscvAirId::Global];
160 let memory = trunc_32(
161 (available_trace_area as usize / cost_per_memory).min(max_height as usize) / 2,
162 );
163 let cost_per_page_prot =
164 costs[&RiscvAirId::PageProtGlobalInit] + costs[&RiscvAirId::Global];
165 let page_prot = trunc_32(
166 (available_trace_area as usize / cost_per_page_prot).min(max_height as usize) / 2,
167 );
168
169 let pack_trace_threshold = 2 * opts.sharding_threshold.element_threshold / 3;
171 let mut combine_memory_threshold =
173 trunc_32(3 * opts.sharding_threshold.element_threshold as usize / cost_per_memory / 40);
174 let mut combine_page_prot_threshold = trunc_32(
175 3 * opts.sharding_threshold.element_threshold as usize / cost_per_page_prot / 40,
176 );
177
178 if !page_protect_allowed {
180 combine_memory_threshold *= 2;
181 combine_page_prot_threshold = 0;
182 }
183
184 Self {
185 pack_trace_threshold,
186 combine_memory_threshold,
187 combine_page_prot_threshold,
188 syscall_threshold,
189 memory,
190 page_prot,
191 }
192 }
193}