-
Notifications
You must be signed in to change notification settings - Fork 690
Expand file tree
/
Copy pathgkr.rs
More file actions
169 lines (156 loc) · 6.62 KB
/
Copy pathgkr.rs
File metadata and controls
169 lines (156 loc) · 6.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
//! Bench logup-GKR populate + prove. Two named bench groups in this file:
//!
//! - `populate_circuit` times [`generate_gkr_circuit`] (build the layer stack from the trace).
//! - `prove` times [`prove_gkr_circuit`] (sumcheck-per-layer + Fiat-Shamir).
//!
//! Both run off the same [`FullKind`] trace source, so the cluster's chips, per-chip
//! interactions, and jagged trace MLE all come from `with_trace_source`. See
//! [`benches/README.md`] for CLI invocations and the source-arg story.
//!
//! Why two bench groups: GKR layers are halved on each transition, so total work is roughly
//! `first_layer + first_layer/2 + first_layer/4 + ... ≈ 2*first_layer` — splitting the bench
//! lets a regression land on the right side (kernel work vs. the per-round CPU/Fiat-Shamir
//! loop) instead of being hidden in a combined number.
use std::collections::BTreeMap;
use std::sync::Arc;
use criterion::{black_box, criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion};
use rand::{rngs::StdRng, Rng, SeedableRng};
use slop_challenger::{FieldChallenger, IopCtx};
use slop_multilinear::Point;
use sp1_core_machine::riscv::RiscvAir;
use sp1_gpu_cudart::TaskScope;
use sp1_gpu_jagged_tracegen::test_utils::bench_utils::{
with_trace_source, FullKind, RealTraceData,
};
use sp1_gpu_jagged_tracegen::test_utils::tracegen_setup::CORE_MAX_LOG_ROW_COUNT;
use sp1_gpu_logup_gkr::{generate_gkr_circuit, prove_logup_gkr, CudaLogUpGkrOptions, Interactions};
use sp1_gpu_utils::{Ext, Felt, TestGC};
use sp1_hypercube::air::MachineAir;
use sp1_hypercube::Chip;
use sp1_primitives::SP1GlobalContext;
/// `prove_gkr_circuit` flag: when true, the prover recomputes the first layer on demand from
/// the raw trace each time it walks back to the leaves; when false, it caches the materialized
/// layer. The end-to-end prover (`prove_logup_gkr`) sets this to true in the existing test, so
/// we match that here for parity. Flip if you want to bench the cache-on path.
const RECOMPUTE_FIRST_LAYER: bool = true;
/// Build the per-chip [`Interactions`] map once per bench setup. Mirrors the loop at the top of
/// `prove_logup_gkr` (`sp1_gpu_logup_gkr::lib::prove_logup_gkr`, lines ~583-588).
fn build_interactions(
cluster: &std::collections::BTreeSet<Chip<Felt, RiscvAir<Felt>>>,
scope: &TaskScope,
) -> BTreeMap<String, Arc<Interactions<Felt, TaskScope>>> {
let mut map = BTreeMap::new();
for chip in cluster.iter() {
let interactions = Interactions::new(chip.sends(), chip.receives());
let device = interactions.copy_to_device(scope).unwrap();
map.insert(chip.name().to_string(), Arc::new(device));
}
map
}
/// `beta_seed` dimension that `prove_logup_gkr` derives from the cluster's max interaction
/// arity. Pulled out so both benches use exactly the same value.
fn beta_seed_dim(cluster: &std::collections::BTreeSet<Chip<Felt, RiscvAir<Felt>>>) -> u32 {
let max_arity = cluster
.iter()
.flat_map(|c| c.sends().iter().chain(c.receives().iter()))
.map(|i| i.values.len() + 1)
.max()
.expect("cluster has no interactions — empty cluster?");
(max_arity as u32).next_power_of_two().ilog2()
}
const GKR_OPTIONS: CudaLogUpGkrOptions = CudaLogUpGkrOptions {
recompute_first_layer: RECOMPUTE_FIRST_LAYER,
num_row_variables: CORE_MAX_LOG_ROW_COUNT,
};
fn run_populate_circuit<R: Rng>(
c: &mut Criterion,
id: BenchmarkId,
scope: &TaskScope,
_rng: &mut R,
data: RealTraceData,
) {
let RealTraceData { machine: _, cluster, public_values: _, device_mle } = data;
let interactions = build_interactions(&cluster, scope);
let beta_dim = beta_seed_dim(&cluster);
let mut group = c.benchmark_group("populate_circuit");
group.sample_size(10);
group.bench_with_input(id, &(), |b, _| {
b.iter_batched(
|| {
// Per-iteration: a fresh challenger sample for (alpha, beta_seed). Cheap on
// CPU; doing it here keeps any per-call randomness state out of the timed
// block. We don't observe the prior trace state — for timing purposes the
// challenger's history is irrelevant, only its sampling cost is.
let mut challenger = TestGC::default_challenger();
let alpha: Ext = challenger.sample_ext_element();
let beta_seed: Point<Ext> =
(0..beta_dim).map(|_| challenger.sample_ext_element::<Ext>()).collect();
scope.synchronize_blocking().unwrap();
(alpha, beta_seed)
},
|(alpha, beta_seed)| {
let result = generate_gkr_circuit(
&cluster,
interactions.clone(),
&device_mle,
alpha,
beta_seed,
GKR_OPTIONS,
scope.clone(),
);
scope.synchronize_blocking().unwrap();
black_box(result)
},
BatchSize::PerIteration,
);
});
group.finish();
}
fn run_prove<R: Rng>(
c: &mut Criterion,
id: BenchmarkId,
scope: &TaskScope,
_rng: &mut R,
data: RealTraceData,
) {
let RealTraceData { machine: _, cluster, public_values: _, device_mle } = data;
let interactions = build_interactions(&cluster, scope);
let mut group = c.benchmark_group("prove");
group.sample_size(10);
group.bench_with_input(id, &(), |b, _| {
b.iter_batched(
|| {
let challenger = TestGC::default_challenger();
scope.synchronize_blocking().unwrap();
(interactions.clone(), challenger)
},
|(interactions, mut challenger)| {
let result = prove_logup_gkr::<SP1GlobalContext, _>(
&cluster,
interactions,
&device_mle,
GKR_OPTIONS,
&mut challenger,
);
scope.synchronize_blocking().unwrap();
black_box(result)
},
BatchSize::PerIteration,
);
});
group.finish();
}
fn bench_populate_circuit(c: &mut Criterion) {
let mut rng = StdRng::seed_from_u64(42);
with_trace_source(c, &mut rng, FullKind, |c, id, scope, rng, data| {
run_populate_circuit(c, id, scope, rng, data);
});
}
fn bench_prove(c: &mut Criterion) {
let mut rng = StdRng::seed_from_u64(42);
with_trace_source(c, &mut rng, FullKind, |c, id, scope, rng, data| {
run_prove(c, id, scope, rng, data);
});
}
criterion_group!(benches, bench_populate_circuit, bench_prove);
criterion_main!(benches);