Compare commits

...

6 Commits

Author SHA1 Message Date
ashWhiteHat fd92875c5b
Merge 24e3ec3633 into 7df93fd855 2024-03-01 11:27:10 -07:00
Daira-Emma Hopwood 7df93fd855
Merge pull request #814 from adria0/fix/mdbook
Fix MD book generation
2024-02-26 23:50:17 +00:00
adria0 daaa638966 fix(mdbook): fix generation 2024-02-22 22:28:36 +01:00
ashWhiteHat 24e3ec3633 arithmetic::best_multiexp refactor buckets 2023-09-06 12:57:57 +09:00
ashWhiteHat e00f0d1233 arithmetic::best_multiexp parallelize bucket arithmetic 2023-09-06 12:39:59 +09:00
ashWhiteHat af1713f1d3 arithmetic::best_multiexp add benchmark 2023-09-06 10:46:30 +09:00
5 changed files with 134 additions and 96 deletions

View File

@ -12,7 +12,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
toolchain: '1.76.0'
override: true
# - name: Setup mdBook
@ -26,7 +26,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: install
args: mdbook --git https://github.com/HollowMan6/mdBook.git --rev 62e01b34c23b957579c04ee1b24b57814ed8a4d5
args: mdbook --git https://github.com/HollowMan6/mdBook.git --rev 5830c9555a4dc051675d17f1fcb04dd0920543e8
- name: Install mdbook-katex and mdbook-pdf
uses: actions-rs/cargo@v1
@ -40,6 +40,11 @@ jobs:
- name: Build halo2 book
run: mdbook build book/
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly-2023-10-05
override: true
- name: Build latest rustdocs
uses: actions-rs/cargo@v1
with:

View File

@ -14,8 +14,6 @@ title = "The halo2 Book"
macros = "macros.txt"
renderers = ["html"]
[output.katex]
[output.html]
[output.html.print]

View File

@ -31,6 +31,10 @@ harness = false
name = "hashtocurve"
harness = false
[[bench]]
name = "msm"
harness = false
[[bench]]
name = "plonk"
harness = false

View File

@ -0,0 +1,27 @@
#[macro_use]
extern crate criterion;
use crate::arithmetic::best_multiexp;
use crate::pasta::{EqAffine, Fp};
use crate::poly::commitment::Params;
use criterion::{BenchmarkId, Criterion};
use group::ff::Field;
use halo2_proofs::*;
use rand_core::OsRng;
fn criterion_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("msm");
for k in 8..16 {
group
.bench_function(BenchmarkId::new("k", k), |b| {
let coeffs = (0..(1 << k)).map(|_| Fp::random(OsRng)).collect::<Vec<_>>();
let bases = Params::<EqAffine>::new(k).get_g();
b.iter(|| best_multiexp(&coeffs, &bases))
})
.sample_size(30);
}
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

View File

@ -7,7 +7,7 @@ use group::{
ff::{BatchInvert, PrimeField},
Group as _, GroupOpsOwned, ScalarMulOwned,
};
use maybe_rayon::prelude::*;
pub use pasta_curves::arithmetic::*;
/// This represents an element of a group with basic operations that can be
@ -25,92 +25,87 @@ where
{
}
fn multiexp_serial<C: CurveAffine>(coeffs: &[C::Scalar], bases: &[C], acc: &mut C::Curve) {
let coeffs: Vec<_> = coeffs.iter().map(|a| a.to_repr()).collect();
#[derive(Clone, Copy)]
enum Bucket<C: CurveAffine> {
None,
Affine(C),
Projective(C::Curve),
}
let c = if bases.len() < 4 {
1
} else if bases.len() < 32 {
3
} else {
(f64::from(bases.len() as u32)).ln().ceil() as usize
};
fn get_at<F: PrimeField>(segment: usize, c: usize, bytes: &F::Repr) -> usize {
let skip_bits = segment * c;
let skip_bytes = skip_bits / 8;
if skip_bytes >= 32 {
return 0;
impl<C: CurveAffine> Bucket<C> {
fn add_assign(&mut self, other: &C) {
*self = match *self {
Bucket::None => Bucket::Affine(*other),
Bucket::Affine(a) => Bucket::Projective(a + *other),
Bucket::Projective(mut a) => {
a += *other;
Bucket::Projective(a)
}
}
let mut v = [0; 8];
for (v, o) in v.iter_mut().zip(bytes.as_ref()[skip_bytes..].iter()) {
*v = *o;
}
let mut tmp = u64::from_le_bytes(v);
tmp >>= skip_bits - (skip_bytes * 8);
tmp %= 1 << c;
tmp as usize
}
let segments = (256 / c) + 1;
for current_segment in (0..segments).rev() {
for _ in 0..c {
*acc = acc.double();
}
#[derive(Clone, Copy)]
enum Bucket<C: CurveAffine> {
None,
Affine(C),
Projective(C::Curve),
}
impl<C: CurveAffine> Bucket<C> {
fn add_assign(&mut self, other: &C) {
*self = match *self {
Bucket::None => Bucket::Affine(*other),
Bucket::Affine(a) => Bucket::Projective(a + *other),
Bucket::Projective(mut a) => {
a += *other;
Bucket::Projective(a)
}
}
}
fn add(self, mut other: C::Curve) -> C::Curve {
match self {
Bucket::None => other,
Bucket::Affine(a) => {
other += a;
other
}
Bucket::Projective(a) => other + &a,
}
fn add(self, mut other: C::Curve) -> C::Curve {
match self {
Bucket::None => other,
Bucket::Affine(a) => {
other += a;
other
}
Bucket::Projective(a) => other + &a,
}
}
}
let mut buckets: Vec<Bucket<C>> = vec![Bucket::None; (1 << c) - 1];
#[derive(Clone)]
struct Buckets<C: CurveAffine> {
c: usize,
coeffs: Vec<Bucket<C>>,
}
impl<C: CurveAffine> Buckets<C> {
fn new(c: usize) -> Self {
Self {
c,
coeffs: vec![Bucket::None; (1 << c) - 1],
}
}
fn sum(&mut self, coeffs: &[C::Scalar], bases: &[C], i: usize) -> C::Curve {
// get segmentation and add coeff to buckets content
for (coeff, base) in coeffs.iter().zip(bases.iter()) {
let coeff = get_at::<C::Scalar>(current_segment, c, coeff);
if coeff != 0 {
buckets[coeff - 1].add_assign(base);
let seg = self.get_at::<C::Scalar>(i, &coeff.to_repr());
if seg != 0 {
self.coeffs[seg - 1].add_assign(base);
}
}
// Summation by parts
// e.g. 3a + 2b + 1c = a +
// (a) + b +
// ((a) + b) + c
let mut running_sum = C::Curve::identity();
for exp in buckets.into_iter().rev() {
running_sum = exp.add(running_sum);
*acc += &running_sum;
let mut acc = C::Curve::identity();
let mut sum = C::Curve::identity();
self.coeffs.iter().rev().for_each(|b| {
sum = b.add(sum);
acc += sum;
});
acc
}
fn get_at<F: PrimeField>(&self, segment: usize, bytes: &F::Repr) -> usize {
let skip_bits = segment * self.c;
let skip_bytes = skip_bits / 8;
if skip_bytes >= 32 {
0
} else {
let mut v = [0; 8];
for (v, o) in v.iter_mut().zip(bytes.as_ref()[skip_bytes..].iter()) {
*v = *o;
}
let mut tmp = u64::from_le_bytes(v);
tmp >>= skip_bits - (skip_bytes * 8);
(tmp % (1 << self.c)) as usize
}
}
}
@ -147,29 +142,38 @@ pub fn small_multiexp<C: CurveAffine>(coeffs: &[C::Scalar], bases: &[C]) -> C::C
pub fn best_multiexp<C: CurveAffine>(coeffs: &[C::Scalar], bases: &[C]) -> C::Curve {
assert_eq!(coeffs.len(), bases.len());
let c = if bases.len() < 4 {
1
} else if bases.len() < 32 {
3
} else {
(f64::from(bases.len() as u32)).ln().ceil() as usize
};
let mut multi_buckets: Vec<Buckets<C>> = vec![Buckets::new(c); (256 / c) + 1];
let num_threads = multicore::current_num_threads();
if coeffs.len() > num_threads {
let chunk = coeffs.len() / num_threads;
let num_chunks = coeffs.chunks(chunk).len();
let mut results = vec![C::Curve::identity(); num_chunks];
multicore::scope(|scope| {
let chunk = coeffs.len() / num_threads;
for ((coeffs, bases), acc) in coeffs
.chunks(chunk)
.zip(bases.chunks(chunk))
.zip(results.iter_mut())
{
scope.spawn(move |_| {
multiexp_serial(coeffs, bases, acc);
});
}
});
results.iter().fold(C::Curve::identity(), |a, b| a + b)
multi_buckets
.par_iter_mut()
.enumerate()
.rev()
.map(|(i, buckets)| {
let mut acc = buckets.sum(coeffs, bases, i);
(0..c * i).for_each(|_| acc = acc.double());
acc
})
.reduce(|| C::Curve::identity(), |a, b| a + b)
} else {
let mut acc = C::Curve::identity();
multiexp_serial(coeffs, bases, &mut acc);
acc
multi_buckets
.iter_mut()
.enumerate()
.rev()
.map(|(i, buckets)| buckets.sum(coeffs, bases, i))
.fold(C::Curve::identity(), |mut sum, bucket| {
// restore original evaluation point
(0..c).for_each(|_| sum = sum.double());
sum + bucket
})
}
}