zksync-gpu-prover 0.155.11

ZKsync GPU prover utilities
use super::*;
use bellman::pairing::{CurveAffine, GenericCurveProjective};
use core::ops::Range;
use franklin_crypto::rand::{thread_rng, Rng};

pub(crate) fn generate_scalars_to_buf<F: PrimeField>(worker: &Worker, buf: &mut [F]) {
    assert!(buf.len().is_power_of_two());
    worker.scope(buf.len(), |scope, chunk_size| {
        for chunk in buf.chunks_mut(chunk_size) {
            scope.spawn(|_| {
                let rng = &mut thread_rng();
                for el in chunk.iter_mut() {
                    *el = rng.gen();
                }
            });
        }
    });
}

pub(crate) fn generate_bases<E: Engine>(worker: &Worker, degree: usize) -> Vec<E::G1Affine> {
    assert!(degree.is_power_of_two());
    let mut bases = vec![E::G1Affine::zero(); degree];
    worker.scope(bases.len(), |scope, chunk_size| {
        for chunk in bases.chunks_mut(chunk_size) {
            scope.spawn(|_| {
                let rng = &mut thread_rng();
                for el in chunk.iter_mut() {
                    *el = rng.gen::<E::G1>().into_affine();
                }
            });
        }
    });

    bases
}

pub(crate) fn generate_dummy_bases<E: Engine>(worker: &Worker, degree: usize) -> Vec<E::G1Affine> {
    assert!(degree.is_power_of_two());
    let mut bases = vec![E::G1Affine::zero(); degree];
    worker.scope(bases.len(), |scope, chunk_size| {
        for chunk in bases.chunks_mut(chunk_size) {
            scope.spawn(|_| {
                for el in chunk.iter_mut() {
                    *el = E::G1Affine::one();
                }
            });
        }
    });

    bases
}

pub fn transmute_values<'a, U, V, const R: bool>(values: &'a [U]) -> &'a [V] {
    let ptr = values.as_ptr();
    let len = values.len();

    assert!(
        (ptr as usize) % std::mem::align_of::<V>() == 0,
        "trying to cast with mismatched layout"
    );

    let size = if R {
        assert!(len % std::mem::size_of::<V>() == 0);
        len / std::mem::size_of::<V>()
    } else {
        std::mem::size_of::<U>() * len
    };

    let out: &'a [V] = unsafe { std::slice::from_raw_parts(ptr as *const V, size) };

    out
}

pub fn transmute_values_mut<'a, U, V, const R: bool>(values: &'a mut [U]) -> &'a mut [V] {
    let ptr = values.as_mut_ptr();
    let len = values.len();

    assert!(
        (ptr as usize) % std::mem::align_of::<V>() == 0,
        "trying to cast with mismatched layout"
    );

    let size = if R {
        assert!(len % std::mem::size_of::<V>() == 0);
        len / std::mem::size_of::<V>()
    } else {
        std::mem::size_of::<U>() * len
    };

    let out: &'a mut [V] = unsafe { std::slice::from_raw_parts_mut(ptr as *mut V, size) };

    out
}

pub fn async_copy<T: Copy + Send + Sync>(worker: &Worker, dest: &mut [T], src: &[T]) {
    let length = dest.len();
    assert_eq!(length, src.len());

    worker.scope(length, |scope, chunk_size| {
        for (range, chunk) in chunks_mut_with_ranges!(chunk_size, dest) {
            let src_ref = &src;
            scope.spawn(move |_| {
                chunk.copy_from_slice(&src_ref[range]);
            });
        }
    });
}

pub fn fill_with_zeros<F: PrimeField>(worker: &Worker, dest: &mut [F]) {
    fill_with(worker, dest, F::zero());
}

pub fn fill_with_ones<F: PrimeField>(worker: &Worker, dest: &mut [F]) {
    fill_with(worker, dest, F::one());
}

pub fn fill_with<T: Copy + Send>(worker: &Worker, dest: &mut [T], value: T) {
    let length = dest.len();
    assert!(length > 0);
    worker.scope(length, |scope, chunk_size| {
        for (_, chunk) in chunks_mut_with_ranges!(chunk_size, dest) {
            scope.spawn(move |_| {
                for el in chunk.iter_mut() {
                    *el = value;
                }
            });
        }
    });
}

#[cfg(feature = "allocator")]
pub fn empty_vec<T, A: Allocator + Default>(len: usize) -> std::vec::Vec<T, A> {
    let mut res = Vec::with_capacity_in(len, A::default());
    unsafe {
        res.set_len(len);
    }
    res
}
#[cfg(not(feature = "allocator"))]
pub fn empty_vec<T>(len: usize) -> Vec<T> {
    let mut res = Vec::with_capacity(len);
    unsafe {
        res.set_len(len);
    }
    res
}

#[macro_export]
macro_rules! chunks_mut_with_ranges{
    ($cl:expr, $first:expr) => {
        {
            let length = $first.len();
            itertools::izip!(
                ranges_from_length_and_chunk_size(length, $cl).into_iter(),
                $first.chunks_mut($cl)
            )
        }
    };
    ($cl:expr, $first:expr $(, $v:expr)+ ) => {
        {
            let length = $first.len();

            ranges_from_length_and_chunk_size(length, $cl).into_iter().zip(
                itertools::izip!(
                    $first.chunks_mut($cl)
                    $(
                    , $v.chunks_mut($cl)
                    )*
                )
            )
        }
    }
}

pub fn ranges_from_length_and_chunk_size(length: usize, chunk_size: usize) -> Vec<Range<usize>> {
    assert!(chunk_size > 0);
    if length == 0 {
        return vec![];
    }

    let mut result = vec![];
    let mut current_start = 0;

    while (current_start + chunk_size) < length {
        result.push(current_start..(current_start + chunk_size));
        current_start += chunk_size;
    }
    result.push(current_start..length);

    result
}

pub fn async_sort<T: Ord + Send + Copy>(worker: &Worker, vals: &mut Vec<T>) {
    let length = vals.len();
    let num_cpus = if let Ok(num_cpus) = std::env::var("NUM_CPUS") {
        num_cpus.parse().unwrap()
    } else {
        num_cpus::get()
    };

    let mut chunk_size = get_chunk_size(num_cpus, length);

    crossbeam::scope(|scope| {
        for chunk in vals.chunks_mut(chunk_size) {
            scope.spawn(move |_| {
                chunk.sort();
            });
        }
    })
    .expect("must run sorting chunks");

    while chunk_size < length {
        crossbeam::scope(|scope| {
            for chunks in vals.chunks_mut(2 * chunk_size) {
                scope.spawn(move |_| {
                    if chunks.len() > chunk_size {
                        let mut chunks: Vec<&mut [T]> = chunks.chunks_mut(chunk_size).collect();
                        let chunk2 = chunks.pop().unwrap();
                        let chunk1 = chunks.pop().unwrap();
                        sorting_merge(chunk1, chunk2);
                    }
                });
            }
        })
        .expect("must run merging sorted chunks");

        chunk_size *= 2;
    }
}

fn sorting_merge<T: Ord + Copy>(vals1: &mut [T], vals2: &mut [T]) {
    let len1 = vals1.len();
    let len2 = vals2.len();
    let length = len1 + len2;
    let mut res = Vec::with_capacity(length);

    let (mut i, mut j) = (0, 0);

    for _ in 0..length {
        if i == len1 {
            res.push(vals2[j]);
            j += 1;
        } else if j == len2 {
            res.push(vals1[i]);
            i += 1;
        } else if vals1[i] > vals2[j] {
            res.push(vals2[j]);
            j += 1;
        } else {
            res.push(vals1[i]);
            i += 1;
        }
    }

    vals1.copy_from_slice(&res[..len1]);
    vals2.copy_from_slice(&res[len1..]);
}

pub fn get_chunk_size(num_cpus: usize, elements: usize) -> usize {
    let chunk_size = if elements <= num_cpus {
        1
    } else {
        assert!(
            elements >= num_cpus,
            "received {} elements to spawn {} threads",
            elements,
            num_cpus
        );
        if elements % num_cpus == 0 {
            elements / num_cpus
        } else {
            elements / num_cpus + 1
        }
    };

    chunk_size
}

use bit_vec::BitVec;
pub fn bitvec_to_field_buffer(worker: &Worker, bitvec: &BitVec, buffer: &mut [Fr]) {
    worker.scope(bitvec.len(), |scope, chunk_len| {
        for (i, chunk) in buffer.chunks_mut(chunk_len).enumerate() {
            let bitvec_ref = &bitvec;
            scope.spawn(move |_| {
                for (bit, el) in bitvec_ref.iter().skip(i * chunk_len).zip(chunk.iter_mut()) {
                    if bit {
                        *el = Fr::one();
                    } else {
                        *el = Fr::zero();
                    }
                }
            });
        }
    });
}

pub fn vec_of_bits_to_field_buffer(worker: &Worker, bitvec: &Vec<bool>, buffer: &mut [Fr]) {
    worker.scope(bitvec.len(), |scope, chunk_len| {
        for (i, chunk) in buffer.chunks_mut(chunk_len).enumerate() {
            let bitvec_ref = &bitvec;
            scope.spawn(move |_| {
                for (bit, el) in bitvec_ref.iter().skip(i * chunk_len).zip(chunk.iter_mut()) {
                    if *bit {
                        *el = Fr::one();
                    } else {
                        *el = Fr::zero();
                    }
                }
            });
        }
    });
}

#[cfg(not(feature = "allocator"))]
macro_rules! new_vec_with_allocator {
    ($capacity:expr) => {
        Vec::with_capacity($capacity)
    };
}

#[cfg(feature = "allocator")]
macro_rules! new_vec_with_allocator {
    ($capacity:expr) => {
        Vec::with_capacity_in($capacity, A::default())
    };
}