Skip to content

Commit

Permalink
Refactor Supernova benchmarks and config for CI (#253)
Browse files Browse the repository at this point in the history
* Refactor Supernova benchmarks and config for CI

* Enable `asm` feature in GPU CI benchmarks

* Clean up and address feedback
  • Loading branch information
samuelburnham authored Jan 12, 2024
1 parent 66669b9 commit 2066ae4
Show file tree
Hide file tree
Showing 11 changed files with 489 additions and 602 deletions.
2 changes: 1 addition & 1 deletion .github/PERF_REGRESSION.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
title: ":rotating_light: Performance regression in #{{ env.PR_NUMBER }}"
labels: P-Performance, automated issue
---
Regression >= {{ env.NOISE_THRESHOLD }} found during merge of: #{{ env.PR_NUMBER }}
Regression >= {{ env.NOISE_THRESHOLD }}% found during merge of: #{{ env.PR_NUMBER }}
Commit: {{ env.GIT_SHA }}
Triggered by: {{ env.WORKFLOW_URL }}
39 changes: 19 additions & 20 deletions .github/workflows/gpu-bench.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Run final tests only when attempting to merge, shown as skipped status checks beforehand
# Run regression check only when attempting to merge, shown as skipped status check beforehand
name: GPU benchmark regression test

on:
Expand Down Expand Up @@ -39,6 +39,7 @@ jobs:
- name: Set bench output format and base SHA
run: |
echo "ARECIBO_BENCH_OUTPUT=commit-comment" | tee -a $GITHUB_ENV
echo "ARECIBO_BENCH_NUM_CONS=16384,1038732" | tee -a $GITHUB_ENV
echo "BASE_COMMIT=${{ github.event.merge_group.base_sha }}" | tee -a $GITHUB_ENV
GPU_NAME=$(nvidia-smi --query-gpu=gpu_name --format=csv,noheader,nounits | tail -n1)
echo "GPU_ID=$(echo $GPU_NAME | awk '{ print $NF }')" | tee -a $GITHUB_ENV
Expand All @@ -54,14 +55,14 @@ jobs:
# Copy justfile to dev, overwriting existing config with that of PR branch
cp ../benches/justfile .
# Run benchmark
just gpu-bench-ci recursive-snark recursive-snark-supernova compressed-snark compressed-snark-supernova
just gpu-bench-ci supernova-ci
# Copy bench output to PR branch
cp *-${{ env.BASE_COMMIT }}.json ..
cp supernova-ci-${{ env.BASE_COMMIT }}.json ..
working-directory: ${{ github.workspace }}/dev
- name: Run GPU bench on PR branch
run: |
just gpu-bench-ci recursive-snark recursive-snark-supernova compressed-snark compressed-snark-supernova
cp *-${{ github.sha }}.json ..
just gpu-bench-ci supernova-ci
cp supernova-ci-${{ github.sha }}.json ..
working-directory: ${{ github.workspace }}/benches
- name: copy the benchmark template and prepare it with data
run: |
Expand All @@ -77,44 +78,42 @@ jobs:
# Use conditionals to ensure that only non-empty variables are inserted
[[ ! -z "${{ env.GPU_NAME }}" ]] && sed -i "/^\"\"\"$/i ${{ env.GPU_NAME }}" tables.toml
[[ ! -z "$CPU_MODEL" ]] && sed -i "/^\"\"\"$/i $CPU_MODEL" tables.toml
[[ ! -z "$NUM_VCPUS" ]] && sed -i "/^\"\"\"$/i $NUM_VCPUS" tables.toml
[[ ! -z "$NUM_VCPUS" ]] && sed -i "/^\"\"\"$/i $NUM_VCPUS vCPUs" tables.toml
[[ ! -z "$TOTAL_RAM" ]] && sed -i "/^\"\"\"$/i $TOTAL_RAM" tables.toml
sed -i "/^\"\"\"$/i Workflow run: $WORKFLOW_URL" tables.toml
echo "WORKFLOW_URL=$WORKFLOW_URL" | tee -a $GITHUB_ENV
working-directory: ${{ github.workspace }}
# Create a `criterion-table` and write in commit comment
- name: Run `criterion-table`
run: |
cat recursive-snark-${{ env.BASE_COMMIT }}.json recursive-snark-${{ github.sha }}.json \
recursive-snark-supernova-${{ env.BASE_COMMIT }}.json recursive-snark-supernova- ${{ github.sha }}.json \
compressed-snark-${{ env.BASE_COMMIT }}.json compressed-snark-${{ github.sha }}.json \
compressed-snark-supernova-${{ env.BASE_COMMIT }}.json compressed-snark-supernova- ${{ github.sha }}.json \
| criterion-table > BENCHMARKS.md
cat supernova-ci-${{ env.BASE_COMMIT }}.json | criterion-table > BENCHMARKS.md
- name: Write bench on commit comment
uses: peter-evans/commit-comment@v3
with:
body-path: BENCHMARKS.md
# Check for a slowdown >= `$ARECIBO_NOISE_THRESHOLD` (fallback is 5%). If so, open an issue but don't block merge
# Check for a slowdown >= `$ARECIBO_BENCH_NOISE_THRESHOLD` (fallback is 30%/1.3x). If so, open an issue but don't block merge
# Since we are parsing for slowdowns, we simply add 1 to the noise threshold decimal to get the regression factor
- name: Check for perf regression
id: regression-check
run: |
REGRESSIONS=$(awk -F'[*x]' '/slower/{print $12}' BENCHMARKS.md)
echo $regressions
REGRESSIONS=$(grep -o '[0-9.]*x slower' BENCHMARKS.md | cut -d 'x' -f1)
echo $REGRESSIONS
if [ ! -z "${{ env.ARECIBO_NOISE_THRESHOLD}}" ]; then
NOISE_THRESHOLD=$(echo "1+${{ env.ARECIBO_NOISE_THRESHOLD }}" | bc)
if [ ! -z "${{ env.ARECIBO_BENCH_NOISE_THRESHOLD}}" ]; then
REGRESSION_FACTOR=$(echo "${{ env.ARECIBO_BENCH_NOISE_THRESHOLD }}+1" | bc)
else
NOISE_THRESHOLD=1.05
REGRESSION_FACTOR=1.3
fi
for r in $REGRESSIONS
do
if (( $(echo "$r >= $NOISE_THRESHOLD" | bc -l) ))
if (( $(echo "$r >= $REGRESSION_FACTOR" | bc -l) ))
then
exit 1
fi
done
echo "NOISE_THRESHOLD=$NOISE_THRESHOLD" | tee -a $GITHUB_ENV
echo "NOISE_THRESHOLD=$("(REGRESSION_FACTOR-1)*100" | bc) | tee -a $GITHUB_ENV
continue-on-error: true
# Not possible to use ${{ github.event.number }} with the `merge_group` trigger
- name: Get PR number from merge branch
Expand All @@ -126,6 +125,6 @@ jobs:
PR_NUMBER: ${{ env.PR_NUMBER }}
GIT_SHA: ${{ github.sha }}
WORKFLOW_URL: ${{ env.WORKFLOW_URL }}
NOISE_THRESHOLD: $${{ env.NOISE_THRESHOLD }}
NOISE_THRESHOLD: ${{ env.NOISE_THRESHOLD }}
with:
filename: .github/PERF_REGRESSION.md
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ harness = false
name = "compressed-snark-supernova"
harness = false

[[bench]]
name = "supernova-ci"
harness = false

[[bench]]
name = "pcs"
harness = false
Expand Down
11 changes: 7 additions & 4 deletions benches/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
pub mod supernova;

use anyhow::anyhow;
use criterion::BenchmarkId;

Expand All @@ -10,7 +12,7 @@ pub(crate) struct BenchParams {
}
impl BenchParams {
pub(crate) fn bench_id(&self, name: &str) -> BenchmarkId {
let output_type = bench_output_env().unwrap_or("stdout".into());
let output_type = output_type_env().unwrap_or("stdout".into());
match output_type.as_ref() {
"pr-comment" => BenchmarkId::new(name, format!("StepCircuitSize-{}", self.step_size)),
"commit-comment" => BenchmarkId::new(
Expand All @@ -29,13 +31,14 @@ impl BenchParams {
}
}

fn bench_output_env() -> anyhow::Result<String> {
std::env::var("ARECIBO_BENCH_OUTPUT").map_err(|e| anyhow!("Bench output env var isn't set: {e}"))
fn output_type_env() -> anyhow::Result<String> {
std::env::var("ARECIBO_BENCH_OUTPUT")
.map_err(|e| anyhow!("ARECIBO_BENCH_OUTPUT env var isn't set: {e}"))
}

pub(crate) fn noise_threshold_env() -> anyhow::Result<f64> {
std::env::var("ARECIBO_BENCH_NOISE_THRESHOLD")
.map_err(|e| anyhow!("Noise threshold env var isn't set: {e}"))
.map_err(|e| anyhow!("ARECIBO_BENCH_NOISE_THRESHOLD env var isn't set: {e}"))
.and_then(|nt| {
nt.parse::<f64>()
.map_err(|e| anyhow!("Failed to parse noise threshold: {e}"))
Expand Down
181 changes: 181 additions & 0 deletions benches/common/supernova/bench.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
// Code is considered dead unless used in all benchmark targets
#![allow(dead_code)]

use crate::common::supernova::{
num_cons, NonUniformBench, SnarkType, E1, E2, NUM_CONS_VERIFIER_CIRCUIT_PRIMARY, NUM_SAMPLES,
};
use crate::common::{noise_threshold_env, BenchParams};
use arecibo::{
provider::{PallasEngine, VestaEngine},
supernova::NonUniformCircuit,
supernova::TrivialTestCircuit,
supernova::{snark::CompressedSNARK, PublicParams, RecursiveSNARK},
traits::{
snark::RelaxedR1CSSNARKTrait,
snark::{default_ck_hint, BatchedRelaxedR1CSSNARKTrait},
Engine,
},
};
use criterion::{measurement::WallTime, *};

/// Benchmarks the SNARK at a provided number of constraints
///
/// Parameters
/// - `num_augmented_circuits`: the number of augmented circuits in this configuration
/// - `group`: the criterion benchmark group
/// - `num_cons`: the number of constraints in the step circuit
pub fn bench_snark_internal_with_arity<
S1: BatchedRelaxedR1CSSNARKTrait<E1>,
S2: RelaxedR1CSSNARKTrait<E2>,
>(
group: &mut BenchmarkGroup<'_, WallTime>,
num_augmented_circuits: usize,
num_cons: usize,
snark_type: SnarkType,
) {
let bench: NonUniformBench<E1, E2, TrivialTestCircuit<<E2 as Engine>::Scalar>> = match snark_type
{
SnarkType::Recursive => NonUniformBench::new(2, num_cons),
SnarkType::Compressed => NonUniformBench::new(num_augmented_circuits, num_cons),
};
let pp = match snark_type {
SnarkType::Recursive => PublicParams::setup(&bench, &*default_ck_hint(), &*default_ck_hint()),
SnarkType::Compressed => PublicParams::setup(&bench, &*S1::ck_floor(), &*S2::ck_floor()),
};

// TODO: Can we use the same number of warmup steps for recursive and compressed?
let num_warmup_steps = match snark_type {
SnarkType::Recursive => 10,
SnarkType::Compressed => 3,
};
let z0_primary = vec![<E1 as Engine>::Scalar::from(2u64)];
let z0_secondary = vec![<E2 as Engine>::Scalar::from(2u64)];
let mut recursive_snark_option: Option<RecursiveSNARK<E1, E2>> = None;
let mut selected_augmented_circuit = 0;

for _ in 0..num_warmup_steps {
let mut recursive_snark = recursive_snark_option.unwrap_or_else(|| {
RecursiveSNARK::new(
&pp,
&bench,
&bench.primary_circuit(0),
&bench.secondary_circuit(),
&z0_primary,
&z0_secondary,
)
.unwrap()
});

if selected_augmented_circuit == 0 || selected_augmented_circuit == 1 {
recursive_snark
.prove_step(
&pp,
&bench.primary_circuit(selected_augmented_circuit),
&bench.secondary_circuit(),
)
.expect("Prove step failed");

recursive_snark
.verify(&pp, &z0_primary, &z0_secondary)
.expect("Verify failed");
} else {
unimplemented!()
}

selected_augmented_circuit = (selected_augmented_circuit + 1) % num_augmented_circuits;
recursive_snark_option = Some(recursive_snark)
}

assert!(recursive_snark_option.is_some());
let recursive_snark = recursive_snark_option.unwrap();

let bench_params = BenchParams {
step_size: num_cons,
date: env!("VERGEN_GIT_COMMIT_DATE"),
sha: env!("VERGEN_GIT_SHA"),
};

match snark_type {
SnarkType::Compressed => {
let (prover_key, verifier_key) = CompressedSNARK::<_, _, _, _, S1, S2>::setup(&pp).unwrap();
// Benchmark the prove time
group.bench_function(bench_params.bench_id("Prove"), |b| {
b.iter(|| {
assert!(CompressedSNARK::<_, _, _, _, S1, S2>::prove(
black_box(&pp),
black_box(&prover_key),
black_box(&recursive_snark)
)
.is_ok());
})
});

let res = CompressedSNARK::<_, _, _, _, S1, S2>::prove(&pp, &prover_key, &recursive_snark);
assert!(res.is_ok());
let compressed_snark = res.unwrap();
// Benchmark the verification time
group.bench_function(bench_params.bench_id("Verify"), |b| {
b.iter(|| {
assert!(black_box(&compressed_snark)
.verify(
black_box(&pp),
black_box(&verifier_key),
black_box(&z0_primary),
black_box(&z0_secondary),
)
.is_ok());
})
});
}
SnarkType::Recursive => {
// Benchmark the prove time
group.bench_function(bench_params.bench_id("Prove"), |b| {
b.iter(|| {
assert!(black_box(&mut recursive_snark.clone())
.prove_step(
black_box(&pp),
&bench.primary_circuit(0),
&bench.secondary_circuit()
)
.is_ok());
})
});

// Benchmark the verification time
group.bench_function(bench_params.bench_id("Verify"), |b| {
b.iter(|| {
assert!(black_box(&mut recursive_snark.clone())
.verify(
black_box(&pp),
black_box(&[<PallasEngine as Engine>::Scalar::from(2u64)]),
black_box(&[<VestaEngine as Engine>::Scalar::from(2u64)]),
)
.is_ok());
})
});
}
}
}

pub fn run_bench<S1: BatchedRelaxedR1CSSNARKTrait<E1>, S2: RelaxedR1CSSNARKTrait<E2>>(
c: &mut Criterion,
group_name: &str,
arity: usize,
snark_type: SnarkType,
) {
// we vary the number of constraints in the step circuit
for &num_cons_in_augmented_circuit in num_cons().iter() {
// number of constraints in the step circuit
let num_cons = num_cons_in_augmented_circuit
.checked_sub(NUM_CONS_VERIFIER_CIRCUIT_PRIMARY)
.expect("Negative `num_cons`, constraint numbers out of date!");

let mut group = c.benchmark_group(group_name);
group.sample_size(NUM_SAMPLES);
group.noise_threshold(noise_threshold_env().unwrap_or(0.3));

bench_snark_internal_with_arity::<S1, S2>(&mut group, arity, num_cons, snark_type);

group.finish();
}
}
Loading

0 comments on commit 2066ae4

Please sign in to comment.