Skip to content

Commit 80dc2ba

Browse files
authored
fix: take program air height into account in metered execution (#2137)
- add `ProgramAir` height as a constant trace height in metered execution doesn't seem to affect the [benchmark](https://github.com/axiom-crypto/openvm-reth-benchmark/actions/runs/17747443362) which is good
1 parent 69bb1e1 commit 80dc2ba

File tree

11 files changed

+64
-39
lines changed

11 files changed

+64
-39
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ All notable changes to OpenVM will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project follows a versioning principles documented in [VERSIONING.md](./VERSIONING.md).
77

8+
## Unreleased
9+
10+
### Changed
11+
- (Executor) Modified `VirtualMachine::build_metered_ctx` to take the program (`&VmExe<Val<E::SC>>`) as an argument.
12+
813
## v1.4.0 (2025-09-01)
914

1015
### Added

benchmarks/execute/benches/execute.rs

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ use openvm_bigint_circuit::{Int256, Int256CpuProverExt, Int256Executor};
1616
use openvm_bigint_transpiler::Int256TranspilerExtension;
1717
use openvm_circuit::{
1818
arch::{
19-
execution_mode::{MeteredCostCtx, MeteredCtx},
20-
instructions::exe::VmExe,
21-
interpreter::InterpretedInstance,
19+
execution_mode::MeteredCostCtx, instructions::exe::VmExe, interpreter::InterpretedInstance,
2220
ContinuationVmProof, *,
2321
},
2422
derive::VmConfig,
@@ -57,6 +55,7 @@ use openvm_stark_sdk::{
5755
openvm_stark_backend::{
5856
self,
5957
config::{StarkGenericConfig, Val},
58+
keygen::types::MultiStarkProvingKey,
6059
p3_field::PrimeField32,
6160
proof::Proof,
6261
prover::{
@@ -86,7 +85,7 @@ const APP_PROGRAMS: &[&str] = &[
8685
const LEAF_VERIFIER_PROGRAMS: &[&str] = &["kitchen-sink"];
8786
const INTERNAL_VERIFIER_PROGRAMS: &[&str] = &["fibonacci"];
8887

89-
static METERED_CTX: OnceLock<(MeteredCtx, Vec<usize>)> = OnceLock::new();
88+
static VM_PROVING_KEY: OnceLock<MultiStarkProvingKey<SC>> = OnceLock::new();
9089
static METERED_COST_CTX: OnceLock<(MeteredCostCtx, Vec<usize>)> = OnceLock::new();
9190
static EXECUTOR: OnceLock<VmExecutor<BabyBear, ExecuteConfig>> = OnceLock::new();
9291

@@ -231,22 +230,22 @@ fn load_program_executable(program: &str) -> Result<VmExe<BabyBear>> {
231230
Ok(VmExe::from_elf(elf, transpiler)?)
232231
}
233232

234-
fn metering_setup() -> &'static (MeteredCtx, Vec<usize>) {
235-
METERED_CTX.get_or_init(|| {
233+
fn vm_proving_key() -> &'static MultiStarkProvingKey<SC> {
234+
VM_PROVING_KEY.get_or_init(|| {
236235
let config = ExecuteConfig::default();
237236
let engine = BabyBearPoseidon2Engine::new(FriParameters::standard_fast());
238-
let (vm, _) = VirtualMachine::new_with_keygen(engine, ExecuteBuilder, config).unwrap();
239-
let ctx = vm.build_metered_ctx();
240-
let executor_idx_to_air_idx = vm.executor_idx_to_air_idx();
241-
(ctx, executor_idx_to_air_idx)
237+
let circuit = config.create_airs().expect("Failed to create AIRs");
238+
circuit.keygen(&engine)
242239
})
243240
}
244241

245242
fn metered_cost_setup() -> &'static (MeteredCostCtx, Vec<usize>) {
246243
METERED_COST_CTX.get_or_init(|| {
247244
let config = ExecuteConfig::default();
248245
let engine = BabyBearPoseidon2Engine::new(FriParameters::standard_fast());
249-
let (vm, _) = VirtualMachine::new_with_keygen(engine, ExecuteBuilder, config).unwrap();
246+
let pk = vm_proving_key();
247+
let d_pk = engine.device().transport_pk_to_device(pk);
248+
let vm = VirtualMachine::new(engine, ExecuteBuilder, config, d_pk).unwrap();
250249
let ctx = vm.build_metered_cost_ctx();
251250
let executor_idx_to_air_idx = vm.executor_idx_to_air_idx();
252251
(ctx, executor_idx_to_air_idx)
@@ -280,9 +279,16 @@ fn benchmark_execute_metered(bencher: Bencher, program: &str) {
280279
bencher
281280
.with_inputs(|| {
282281
let exe = load_program_executable(program).expect("Failed to load program executable");
283-
let (ctx, executor_idx_to_air_idx) = metering_setup();
282+
let config = ExecuteConfig::default();
283+
let engine = BabyBearPoseidon2Engine::new(FriParameters::standard_fast());
284+
let pk = vm_proving_key();
285+
let d_pk = engine.device().transport_pk_to_device(pk);
286+
let vm = VirtualMachine::new(engine, ExecuteBuilder, config, d_pk).unwrap();
287+
let executor_idx_to_air_idx = vm.executor_idx_to_air_idx();
288+
289+
let ctx = vm.build_metered_ctx(&exe);
284290
let interpreter = executor()
285-
.metered_instance(&exe, executor_idx_to_air_idx)
291+
.metered_instance(&exe, &executor_idx_to_air_idx)
286292
.unwrap();
287293
(interpreter, vec![], ctx.clone())
288294
})
@@ -403,7 +409,7 @@ fn benchmark_leaf_verifier_execute_metered(bencher: Bencher, program: &str) {
403409
bencher
404410
.with_inputs(|| {
405411
let (vm, leaf_exe, input_stream) = setup_leaf_verifier(program);
406-
let ctx = vm.build_metered_ctx();
412+
let ctx = vm.build_metered_ctx(&leaf_exe);
407413
let executor_idx_to_air_idx = vm.executor_idx_to_air_idx();
408414
let interpreter = vm
409415
.executor()
@@ -459,7 +465,7 @@ fn benchmark_internal_verifier_execute_metered(bencher: Bencher, program: &str)
459465
bencher
460466
.with_inputs(|| {
461467
let (vm, internal_exe, input_stream) = setup_internal_verifier(program);
462-
let ctx = vm.build_metered_ctx();
468+
let ctx = vm.build_metered_ctx(&internal_exe);
463469
let executor_idx_to_air_idx = vm.executor_idx_to_air_idx();
464470
let interpreter = vm
465471
.executor()

benchmarks/execute/src/execute-verifier.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ fn execute_verifier(
332332
}
333333
ExecutionMode::Metered => {
334334
tracing::info!("Running metered execute...");
335-
let ctx = vm.build_metered_ctx();
335+
let ctx = vm.build_metered_ctx(exe);
336336
let interpreter = vm.metered_interpreter(exe)?;
337337
interpreter.execute_metered(input_stream, ctx)?;
338338
}

benchmarks/prove/src/bin/kitchen_sink.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ fn verify_native_max_trace_heights(
3131
for leaf_input in leaf_inputs {
3232
let exe = leaf_prover.exe().clone();
3333
let vm = &mut leaf_prover.vm;
34-
let metered_ctx = vm.build_metered_ctx();
34+
let metered_ctx = vm.build_metered_ctx(&exe);
3535
let (segments, _) = vm
3636
.metered_interpreter(&exe)?
3737
.execute_metered(leaf_input.write_to_stream(), metered_ctx)?;

crates/sdk/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ where
375375
let vm = app_prover.vm();
376376
let exe = app_prover.exe();
377377

378-
let ctx = vm.build_metered_ctx();
378+
let ctx = vm.build_metered_ctx(&exe);
379379
let interpreter = vm
380380
.metered_interpreter(&exe)
381381
.map_err(VirtualMachineError::from)?;

crates/vm/src/arch/execution_mode/metered/ctx.rs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::num::NonZero;
22

3+
use itertools::Itertools;
34
use openvm_instructions::riscv::{RV32_IMM_AS, RV32_REGISTER_AS};
45

56
use super::{
@@ -152,17 +153,20 @@ impl<const PAGE_BITS: usize> MeteredCtx<PAGE_BITS> {
152153
}
153154

154155
#[allow(dead_code)]
155-
pub fn print_heights(&self) {
156-
println!("{:>10} {:<30}", "Height", "Air Name");
157-
println!("{}", "-".repeat(42));
158-
for (i, height) in self.trace_heights.iter().enumerate() {
159-
let air_name = self
160-
.segmentation_ctx
161-
.air_names
162-
.get(i)
163-
.map(|s| s.as_str())
164-
.unwrap_or("Unknown");
165-
println!("{:>10} {:<30}", height, air_name);
156+
pub fn print_segment(&self) {
157+
println!("{}", "-".repeat(80));
158+
println!("Segment {}", self.segmentation_ctx.segments.len() - 1);
159+
println!("{}", "-".repeat(80));
160+
println!("{:>10} {:>10} {:<30}", "Width", "Height", "Air Name");
161+
println!("{}", "-".repeat(80));
162+
for ((&width, &height), air_name) in self
163+
.segmentation_ctx
164+
.widths
165+
.iter()
166+
.zip_eq(self.trace_heights.iter())
167+
.zip_eq(self.segmentation_ctx.air_names.iter())
168+
{
169+
println!("{:>10} {:>10} {:<30}", width, height, air_name.as_str());
166170
}
167171
}
168172
}

crates/vm/src/arch/execution_mode/metered/segment_ctx.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ impl Default for SegmentationLimits {
4040
pub struct SegmentationCtx {
4141
pub segments: Vec<Segment>,
4242
pub(crate) air_names: Vec<String>,
43-
widths: Vec<usize>,
43+
pub(crate) widths: Vec<usize>,
4444
interactions: Vec<usize>,
4545
pub(crate) segmentation_limits: SegmentationLimits,
4646
pub instret_last_segment_check: u64,
@@ -152,13 +152,13 @@ impl SegmentationCtx {
152152
return false;
153153
}
154154

155-
for (i, (height, is_constant)) in trace_heights
155+
for (i, (&height, is_constant)) in trace_heights
156156
.iter()
157157
.zip(is_trace_height_constant.iter())
158158
.enumerate()
159159
{
160160
// Only segment if the height is not constant and exceeds the maximum height
161-
if !is_constant && *height > self.segmentation_limits.max_trace_height {
161+
if !is_constant && height > self.segmentation_limits.max_trace_height {
162162
let air_name = &self.air_names[i];
163163
tracing::info!(
164164
"Segment {:2} | instret {:9} | chip {} ({}) height ({:8}) > max ({:8})",

crates/vm/src/arch/vm.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -780,8 +780,11 @@ where
780780
}
781781

782782
/// Convenience method to construct a [MeteredCtx] using data from the stored proving key.
783-
pub fn build_metered_ctx(&self) -> MeteredCtx {
784-
let (constant_trace_heights, air_names, widths, interactions): (
783+
pub fn build_metered_ctx(&self, exe: &VmExe<Val<E::SC>>) -> MeteredCtx {
784+
let config = self.config().as_ref();
785+
let program_len = exe.program.num_defined_instructions();
786+
787+
let (mut constant_trace_heights, air_names, widths, interactions): (
785788
Vec<_>,
786789
Vec<_>,
787790
Vec<_>,
@@ -804,6 +807,13 @@ where
804807
})
805808
.multiunzip();
806809

810+
// Program trace is the same for all segments
811+
constant_trace_heights[PROGRAM_AIR_ID] = Some(program_len);
812+
if config.has_public_values_chip() {
813+
// Public values chip is only present when there's a single segment
814+
constant_trace_heights[PUBLIC_VALUES_AIR_ID] = Some(config.num_public_values);
815+
}
816+
807817
self.executor().build_metered_ctx(
808818
&constant_trace_heights,
809819
&air_names,
@@ -975,7 +985,7 @@ where
975985
let input = input.into();
976986
self.reset_state(input.clone());
977987
let vm = &mut self.vm;
978-
let metered_ctx = vm.build_metered_ctx();
988+
let metered_ctx = vm.build_metered_ctx(&self.exe);
979989
let metered_interpreter = vm.metered_interpreter(&self.exe)?;
980990
let (segments, _) = metered_interpreter.execute_metered(input, metered_ctx)?;
981991
let mut proofs = Vec::with_capacity(segments.len());

crates/vm/src/utils/stark_utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ where
114114
let vk = pk.get_vk();
115115
let exe = exe.into();
116116
let input = input.into();
117-
let metered_ctx = vm.build_metered_ctx();
117+
let metered_ctx = vm.build_metered_ctx(&exe);
118118
let (segments, _) = vm
119119
.metered_interpreter(&exe)?
120120
.execute_metered(input.clone(), metered_ctx)?;

extensions/native/circuit/src/utils.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@ pub mod test_utils {
9797
let input = input_stream.into();
9898

9999
let engine = E::new(FriParameters::new_for_testing(1));
100-
let (vm, _) = VirtualMachine::new_with_keygen(engine, builder, config)?;
101-
let ctx = vm.build_metered_ctx();
102100
let exe = VmExe::new(program);
101+
let (vm, _) = VirtualMachine::new_with_keygen(engine, builder, config)?;
102+
let ctx = vm.build_metered_ctx(&exe);
103103
let (mut segments, _) = vm
104104
.metered_interpreter(&exe)?
105105
.execute_metered(input.clone(), ctx)?;

0 commit comments

Comments
 (0)