Skip to main content

Overview

The analyze_<function_name> function executes the guest program with given inputs and returns detailed statistics about the execution trace. This is useful for understanding program behavior, optimizing performance, and estimating proof generation costs.

Function Signature

pub fn analyze_<function_name>(
    // ... function arguments (public inputs, untrusted advice, trusted advice)
) -> jolt::host::analyze::ProgramSummary

Parameters

The same parameters as the guest function:
  • Public inputs - Regular function parameters
  • Untrusted advice - Parameters that will be passed as untrusted advice
  • Trusted advice - Parameters that will be passed as trusted advice

Return Value

Returns a ProgramSummary containing:
  • Execution trace statistics
  • Instruction counts by type
  • Memory access patterns
  • Cycle counts
  • Other performance metrics

ProgramSummary API

The returned ProgramSummary provides methods for:

Writing to File

pub fn write_to_file(&self, path: PathBuf) -> Result<(), std::io::Error>
Writes the analysis to a text file for inspection.

Usage Examples

Basic Analysis

// Analyze fibonacci(10)
let program_summary = guest::analyze_fib(10);

// Write analysis to file
program_summary
    .write_to_file("fib_10_analysis.txt".into())
    .expect("Failed to write analysis");

Analyzing Different Input Sizes

// Compare execution characteristics for different inputs
let summary_small = guest::analyze_fib(10);
let summary_medium = guest::analyze_fib(50);
let summary_large = guest::analyze_fib(100);

summary_small.write_to_file("fib_10.txt".into()).unwrap();
summary_medium.write_to_file("fib_50.txt".into()).unwrap();
summary_large.write_to_file("fib_100.txt".into()).unwrap();

Analyzing Complex Inputs

let a = vec![1, 2, 3, 4, 5];
let b = vec![6, 7, 8, 9, 10];
let n = 42;

// Analyze with multiple parameters
let summary = guest::analyze_my_function(n, a, b);
summary.write_to_file("analysis.txt".into()).unwrap();

With Cycle Tracking

Combine with cycle tracking in guest code:
// In guest code:
#[jolt::provable]
fn my_function(n: u32) -> u32 {
    jolt::start_cycle_tracking("phase1");
    let result1 = expensive_computation_1(n);
    jolt::end_cycle_tracking("phase1");
    
    jolt::start_cycle_tracking("phase2");
    let result2 = expensive_computation_2(result1);
    jolt::end_cycle_tracking("phase2");
    
    result2
}

// In host code:
let summary = guest::analyze_my_function(100);
// Summary will include cycle counts for "phase1" and "phase2"
summary.write_to_file("profile.txt".into()).unwrap();

Analysis Output Format

The generated analysis file typically includes:
  • Trace Length - Total number of execution cycles
  • Instruction Breakdown - Count of each RISC-V instruction executed
  • Memory Statistics - RAM and register access patterns
  • Cycle Tracking - Named cycle counts (if using start_cycle_tracking / end_cycle_tracking)
  • Advice Usage - Size of advice tape if advice functions were used

Use Cases

Performance Optimization

Identify expensive operations in your guest code:
let summary = guest::analyze_my_algorithm(input);
summary.write_to_file("before_optimization.txt".into()).unwrap();

// ... optimize the guest code ...

let summary_after = guest::analyze_my_algorithm(input);
summary_after.write_to_file("after_optimization.txt".into()).unwrap();

// Compare the two files to see improvements

Proof Cost Estimation

Estimate proving costs before running the full prover:
let summary = guest::analyze_large_computation(input);
summary.write_to_file("cost_estimate.txt".into()).unwrap();

// Review trace length to estimate proving time
// Trace length correlates with proof generation time

Debugging

Understand program behavior and identify issues:
// Analyze with different inputs to debug unexpected behavior
let summary_working = guest::analyze_function(working_input);
let summary_broken = guest::analyze_function(broken_input);

summary_working.write_to_file("working.txt".into()).unwrap();
summary_broken.write_to_file("broken.txt".into()).unwrap();

// Compare instruction counts and trace patterns

Generated From

For a function annotated with #[jolt::provable]:
#[jolt::provable]
fn fib(n: u32) -> u32 {
    // implementation
}
The macro generates analyze_fib that accepts the same parameters.

Important Notes

  • Analysis executes the guest program but does not generate a proof
  • Much faster than proof generation - useful for rapid iteration
  • Analysis output depends on the specific input - different inputs may have different characteristics
  • The analysis includes all features enabled during compilation (std/no-std, backtrace, profiling)

Profiling Features

The guest program can be built with profiling features:
// Build with backtrace support
#[jolt::provable(backtrace = true)]
fn my_function(n: u32) -> u32 {
    // implementation
}

// Build with profiling
#[jolt::provable(profile = true)]
fn my_function(n: u32) -> u32 {
    // implementation
}
These features affect the analysis output:
  • backtrace = true - Includes stack trace information
  • profile = true - Includes detailed profiling data
  • trace_to_file - Exports raw execution trace to binary file
  • compile - Compiles the guest program
  • Cycle tracking functions in guest code:
    • jolt::start_cycle_tracking(label: &str)
    • jolt::end_cycle_tracking(label: &str)

Build docs developers (and LLMs) love