Skip to content

Commit 6249fd7

Browse files
authored
Merge branch 'main' into feat/merged-bitwise-bus
2 parents a2f4bef + 50b8614 commit 6249fd7

90 files changed

Lines changed: 15589 additions & 1646 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/actions/free-disk/action.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ runs:
55
using: "composite"
66
steps:
77
- name: Remove .NET, Android SDK, Haskell
8+
if: runner.environment == 'github-hosted'
89
shell: bash
910
run: sudo rm -rf /usr/share/dotnet/ /usr/local/lib/android /opt/ghc /usr/local/.ghcup
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: Bench Vs Nightly
2+
3+
on:
4+
schedule:
5+
# 03:00 America/Argentina/Buenos_Aires = 06:00 UTC
6+
- cron: "0 6 * * *"
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: read
11+
12+
concurrency:
13+
group: bench-vs-nightly-${{ github.ref }}
14+
cancel-in-progress: true
15+
16+
jobs:
17+
bench-vs:
18+
runs-on: [self-hosted, bench]
19+
timeout-minutes: 720
20+
steps:
21+
- name: Checkout
22+
uses: actions/checkout@v4
23+
24+
- name: Setup Rust Environment
25+
uses: ./.github/actions/setup-rust
26+
27+
- name: Add cargo to PATH
28+
run: echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
29+
30+
- name: Add SP1 to PATH
31+
run: echo "$HOME/.sp1/bin" >> "$GITHUB_PATH"
32+
33+
- name: Install SP1 toolchain
34+
run: |
35+
export PATH="$HOME/.cargo/bin:$HOME/.sp1/bin:$PATH"
36+
if ! cargo prove --version 2>/dev/null | grep -qE '\b6\.0\.1\b'; then
37+
curl -L https://sp1up.succinct.xyz | bash
38+
export PATH="$HOME/.sp1/bin:$PATH"
39+
sp1up --version v6.0.1
40+
fi
41+
cargo prove --version
42+
43+
- name: Run nightly benchmark
44+
run: |
45+
bash ./bench_vs/run.sh \
46+
--steps 1000000 2000000 4000000 8000000 \
47+
--report-dir bench_vs_artifacts \
48+
--no-color
49+
50+
- name: Upload nightly benchmark artifact
51+
uses: actions/upload-artifact@v4
52+
with:
53+
name: bench-vs-nightly-${{ github.run_number }}-${{ github.sha }}
54+
path: bench_vs_artifacts
55+
retention-days: 90
56+
57+
- name: Publish summary
58+
run: cat bench_vs_artifacts/summary.md >> "$GITHUB_STEP_SUMMARY"

.github/workflows/pr_main.yaml

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ jobs:
7373
path: |
7474
executor/program_artifacts/rust
7575
executor/shared_target
76-
key: rust-elf-artifacts-${{ hashFiles('executor/programs/rust/**', 'executor/programs/riscv64im-lambda-vm-elf.json', 'syscalls/**') }}
76+
key: rust-elf-artifacts-${{ hashFiles('executor/programs/rust/**', 'executor/programs/riscv64im-lambda-vm-elf.json', 'syscalls/**', 'Makefile') }}
7777
restore-keys: |
7878
rust-elf-artifacts-
7979
@@ -187,6 +187,26 @@ jobs:
187187
run: |
188188
make compile-programs-asm
189189
190+
- name: Cache compiled Rust ELF artifacts and build cache
191+
id: cache-rust-elfs
192+
uses: actions/cache@v4
193+
with:
194+
path: |
195+
executor/program_artifacts/rust
196+
executor/shared_target
197+
key: rust-elf-artifacts-${{ hashFiles('executor/programs/rust/**', 'executor/programs/riscv64im-lambda-vm-elf.json', 'syscalls/**', 'Makefile') }}
198+
restore-keys: |
199+
rust-elf-artifacts-
200+
201+
- name: Setup Rust Environment
202+
if: steps.cache-rust-elfs.outputs.cache-hit != 'true'
203+
uses: ./.github/actions/setup-rust
204+
205+
- name: Compile Rust programs to ELF
206+
if: steps.cache-rust-elfs.outputs.cache-hit != 'true'
207+
run: |
208+
make compile-programs-rust
209+
190210
- name: Install nextest
191211
uses: taiki-e/install-action@v2
192212
with:
@@ -229,6 +249,26 @@ jobs:
229249
run: |
230250
make compile-programs-asm
231251
252+
- name: Cache compiled Rust ELF artifacts and build cache
253+
id: cache-rust-elfs
254+
uses: actions/cache@v4
255+
with:
256+
path: |
257+
executor/program_artifacts/rust
258+
executor/shared_target
259+
key: rust-elf-artifacts-${{ hashFiles('executor/programs/rust/**', 'executor/programs/riscv64im-lambda-vm-elf.json', 'syscalls/**', 'Makefile') }}
260+
restore-keys: |
261+
rust-elf-artifacts-
262+
263+
- name: Setup Rust Environment
264+
if: steps.cache-rust-elfs.outputs.cache-hit != 'true'
265+
uses: ./.github/actions/setup-rust
266+
267+
- name: Compile Rust programs to ELF
268+
if: steps.cache-rust-elfs.outputs.cache-hit != 'true'
269+
run: |
270+
make compile-programs-rust
271+
232272
- name: Install nextest
233273
uses: taiki-e/install-action@v2
234274
with:

bench_vs/README.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Lambda VM vs SP1 v6 Benchmark
2+
3+
Compares proving time for an identical u64 wrapping Fibonacci computation.
4+
5+
## Prerequisites
6+
7+
1. **Lambda VM CLI** (built from this repo):
8+
```bash
9+
cargo build --release -p cli
10+
```
11+
12+
2. **SP1 toolchain** (Succinct's prover):
13+
```bash
14+
curl -L https://sp1up.succinct.xyz | bash
15+
sp1up
16+
```
17+
18+
3. **Rust nightly** (for cross-compiling Lambda VM guest):
19+
```bash
20+
rustup toolchain install nightly
21+
```
22+
23+
## Usage
24+
25+
```bash
26+
# Default series: 1k, 10k, 100k, 300k iterations
27+
./bench_vs/run.sh
28+
29+
# Custom series
30+
./bench_vs/run.sh -n 1000 50000
31+
32+
# Approximate workload steps (converted with 5 steps/iteration)
33+
./bench_vs/run.sh --steps 1000000 2000000 4000000 8000000
34+
35+
# Project to a target cycle count
36+
./bench_vs/run.sh --target-cycles 500000000
37+
38+
# Run only one prover
39+
./bench_vs/run.sh --lambda-only
40+
./bench_vs/run.sh --sp1-only
41+
```
42+
43+
## What is measured
44+
45+
Both provers execute the same program: iterative Fibonacci with `u64::wrapping_add`.
46+
47+
The timing window on both sides is **end-to-end single-shot proving, with no
48+
verification and no recursion/compression**. Concretely:
49+
50+
| Phase | Lambda VM timer | SP1 v6 timer |
51+
|--------------------------------------------|:---------------:|:------------:|
52+
| Read ELF + input from disk |||
53+
| Pre-pass execution to count cycles |||
54+
| `setup` / verifying-key derivation | N/A (none) ||
55+
| ELF parse + guest execution (inside prove) |||
56+
| Trace build |||
57+
| AIR construction |||
58+
| STARK prove (`core` mode) |||
59+
| Proof serialization / write |||
60+
| Verify |||
61+
62+
Both sides run one extra execution pass **outside** the timer to report dynamic
63+
instruction counts (SP1's `execute(...)` / Lambda's executor pre-pass). This
64+
costs wall-clock time in the CI job but does not inflate the measured proving
65+
time, and the cost is symmetric between the two provers.
66+
67+
Lambda VM uses the default proof options from `prover::prove_with_inputs`
68+
(`GoldilocksCubicProofOptions::with_blowup(2)`, 50 FRI queries). SP1 v6 uses
69+
the `core` proof mode exposed by `sp1-sdk::ProverClient::from_env()`.
70+
71+
## Projection axis
72+
73+
The linear projection uses **measured cycles** per prover — Lambda's executor
74+
log count and SP1's `report.total_instruction_count()`. For Fibonacci the two
75+
values agree to within ~1% (both compile to the same inner loop shape on
76+
RISC-V). When cycle data is missing, the script falls back to the approximate
77+
`target_workload_steps ~= 5 * n` label that was passed on the command line.
78+
79+
## Output
80+
81+
```
82+
=== Summary ===
83+
Program: Fibonacci (u64 wrapping)
84+
85+
Target steps Iterations Lambda (s) Lambda cycles SP1 (s) SP1 cycles Ratio
86+
------------ ---------- ---------- ------------- ------- ---------- -----
87+
1000000 200000 ...s 1004794 ...s 1004794 ...
88+
2000000 400000 ...s 2004794 ...s 2004794 ...
89+
90+
Timing window covers single-shot end-to-end proving; SP1 includes setup; both exclude verification.
91+
Green ratio = Lambda VM faster, Red = SP1 faster
92+
```
93+
94+
With `--report-dir DIR` the script writes:
95+
- `results.tsv` — raw per-run data (`target_steps`, `iterations`, `lambda_time_s`, `lambda_axis_value`, `lambda_cycles`, `sp1_time_s`, `sp1_axis_value`, `sp1_cycles`, `ratio`).
96+
- `metrics.txt` — key=value pairs including `timing_window=setup_plus_end_to_end_prove_no_verify`.
97+
- `summary.md` — the same table plus linear projection to `TARGET_CYCLES` cycles.
98+
- `raw/` — stdout/stderr of every individual run.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[target.riscv64im-lambda-vm-elf]
2+
rustflags = [
3+
"-C", "link-arg=-e",
4+
"-C", "link-arg=main",
5+
"-C", "passes=lower-atomic"
6+
]

bench_vs/lambda/fibonacci/Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[workspace]
2+
3+
[package]
4+
name = "fibonacci-bench"
5+
version = "0.1.0"
6+
edition = "2024"
7+
8+
[dependencies]
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#![no_std]
2+
#![no_main]
3+
4+
use core::arch::asm;
5+
use core::panic::PanicInfo;
6+
7+
const SYSCALL_GET_PRIVATE_INPUTS: u64 = 4;
8+
const SYSCALL_COMMIT: u64 = 64;
9+
const SYSCALL_HALT: u64 = 93;
10+
11+
#[panic_handler]
12+
fn panic(_info: &PanicInfo) -> ! {
13+
loop {}
14+
}
15+
16+
fn read_n() -> u64 {
17+
let mut input = [0u8; 12];
18+
19+
unsafe {
20+
asm!(
21+
"ecall",
22+
in("a0") input.as_mut_ptr(),
23+
in("a7") SYSCALL_GET_PRIVATE_INPUTS,
24+
);
25+
}
26+
27+
let mut n_bytes = [0u8; 8];
28+
n_bytes.copy_from_slice(&input[4..12]);
29+
u64::from_le_bytes(n_bytes)
30+
}
31+
32+
fn commit(bytes: &[u8]) {
33+
unsafe {
34+
asm!(
35+
"ecall",
36+
in("a0") 1u64,
37+
in("a1") bytes.as_ptr(),
38+
in("a2") bytes.len(),
39+
in("a7") SYSCALL_COMMIT,
40+
);
41+
}
42+
}
43+
44+
fn halt() -> ! {
45+
unsafe {
46+
asm!(
47+
"ecall",
48+
in("a0") 0u64,
49+
in("a7") SYSCALL_HALT,
50+
options(noreturn),
51+
);
52+
}
53+
}
54+
55+
#[unsafe(no_mangle)]
56+
pub fn main() -> ! {
57+
let n = read_n();
58+
let mut a: u64 = 0;
59+
let mut b: u64 = 1;
60+
for _ in 0..n {
61+
let c = a.wrapping_add(b);
62+
a = b;
63+
b = c;
64+
}
65+
66+
commit(&b.to_le_bytes());
67+
halt()
68+
}

0 commit comments

Comments
 (0)