Skip to content

Commit

Permalink
feat(bb): towards reduced polynomial memory usage (#7990)
Browse files Browse the repository at this point in the history
See https://hackmd.io/MDcSYZtESay9rI6-Atd0fg for motivation

Another pass on moving polynomials to a form where 'islands' of
non-zeroes are supported.
- introduce a new form of shifts: we start unshifted polynomials at
start_index() == 1 and for shifted polynomials simply shallow copy them
with start_index() == 0. Note more non-trivial copying of polynomials
(heard whispering of this with AVM, or perhaps if we had prover-builder
sharing) would need more flexibility in the array backing
- ensure shifted polynomials are allocated as above
- change operator[] to be .at() everywhere, which is arbitrarily chosen
to be the mutable operator. This is to keep operator[] able to read
outside of strictly-defined bounds as before. For any mutable accesses,
we use at().
- adapt the code to the new shift scheme, this took the majority of the
time
- try out the new abbreviated scheme with 
- PolynomialSpan now replaces std::span in a few cases, namely in
computing commitments. This includes a start index offset. When
computing MSMs this is natural as we just offset the SRS index
(representing the exponent) by start_index, which follows as
conceptually we have 0 values before start_index.

---------

Co-authored-by: ludamad <[email protected]>
  • Loading branch information
ludamad and ludamad0 authored Sep 10, 2024
1 parent 8de1f2a commit 372f23c
Show file tree
Hide file tree
Showing 82 changed files with 2,217 additions and 1,575 deletions.
9 changes: 8 additions & 1 deletion barretenberg/cpp/scripts/_benchmark_remote_lock.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# NOTE: This script is NOT meant to be ran, only sourced.
# This sets up all the necessary machinery to lock ~/BENCHMARK_IN_PROGRESS
#
#

# Function to clean up lock file
function cleanup() {
Expand All @@ -9,6 +9,13 @@ function cleanup() {
}

# Check for existing lock file
for i in {1..10} ; do
if ! ssh $BB_SSH_KEY $BB_SSH_INSTANCE "test -f ~/BENCHMARK_IN_PROGRESS"; then
break # we are able to benchmark
fi
echo "Benchmarking is already in progress. Waiting..."
sleep 10 # wait
done
if ssh $BB_SSH_KEY $BB_SSH_INSTANCE "test -f ~/BENCHMARK_IN_PROGRESS"; then
echo "Benchmarking is already in progress. If htop on the remote machine is not active, ~/BENCHMARK_IN_PROGRESS may need to be deleted."
# Important: Exits the script that called this!
Expand Down
11 changes: 8 additions & 3 deletions barretenberg/cpp/src/barretenberg/bb/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@ const auto current_dir = current_path.filename().string();
*/
void init_bn254_crs(size_t dyadic_circuit_size)
{
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1097): tighter bound needed
// currently using 1.6x points in CRS because of structured polys, see notes for how to minimize
// Must +1 for Plonk only!
auto bn254_g1_data = get_bn254_g1_data(CRS_PATH, dyadic_circuit_size + 1);
auto bn254_g1_data = get_bn254_g1_data(CRS_PATH, dyadic_circuit_size + dyadic_circuit_size * 6 / 10 + 1);
auto bn254_g2_data = get_bn254_g2_data(CRS_PATH);
srs::init_crs_factory(bn254_g1_data, bn254_g2_data);
}
Expand All @@ -72,7 +74,10 @@ void init_bn254_crs(size_t dyadic_circuit_size)
*/
void init_grumpkin_crs(size_t eccvm_dyadic_circuit_size)
{
auto grumpkin_g1_data = get_grumpkin_g1_data(CRS_PATH, eccvm_dyadic_circuit_size);
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1097): tighter bound needed
// currently using 1.6x points in CRS because of structured polys, see notes for how to minimize
auto grumpkin_g1_data =
get_grumpkin_g1_data(CRS_PATH, eccvm_dyadic_circuit_size + eccvm_dyadic_circuit_size * 6 / 10);
srs::init_grumpkin_crs_factory(grumpkin_g1_data);
}

Expand Down Expand Up @@ -638,7 +643,7 @@ void prove(const std::string& bytecodePath, const std::string& witnessPath, cons

acir_proofs::AcirComposer acir_composer{ 0, verbose_logging };
acir_composer.create_circuit(constraint_system, witness);
init_bn254_crs(acir_composer.get_dyadic_circuit_size());
init_bn254_crs(acir_composer.get_dyadic_circuit_size() * 2);
acir_composer.init_proving_key();
auto proof = acir_composer.create_proof();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -455,10 +455,10 @@ void pippenger(State& state)
size_t num_cycles = 1 << static_cast<size_t>(state.range(0));
Polynomial<Fr> pol(num_cycles);
for (size_t i = 0; i < num_cycles; i++) {
*(uint256_t*)&pol[i] = engine.get_random_uint256();
pol[i].self_reduce_once();
pol[i].self_reduce_once();
pol[i].self_reduce_once();
*(uint256_t*)&pol.at(i) = engine.get_random_uint256();
pol.at(i).self_reduce_once();
pol.at(i).self_reduce_once();
pol.at(i).self_reduce_once();
}

auto ck = std::make_shared<CommitmentKey<curve::BN254>>(num_cycles);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ void ipa_open(State& state) noexcept
// Construct the polynomial
Polynomial poly(n);
for (size_t i = 0; i < n; ++i) {
poly[i] = Fr::random_element(&engine);
poly.at(i) = Fr::random_element(&engine);
}
auto x = Fr::random_element(&engine);
auto eval = poly.evaluate(x);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,21 @@ static void construct_proof_megahonk_power_of_2(State& state) noexcept
state, &bb::mock_circuits::generate_basic_arithmetic_circuit<MegaCircuitBuilder>, log2_of_gates);
}

static void get_row_power_of_2(State& state) noexcept
{
auto log2_of_gates = static_cast<size_t>(state.range(0));
size_t gates = 1 << log2_of_gates;
MegaFlavor::ProverPolynomials polynomials{ gates };
for (auto _ : state) {
for (size_t i = 0; i < gates; i++) {
benchmark::DoNotOptimize(polynomials.get_row(i));
}
}
}

// Define benchmarks

// This exists due to an issue where get_row was blowing up in time
BENCHMARK_CAPTURE(construct_proof_megahonk, sha256, &stdlib::generate_sha256_test_circuit<MegaCircuitBuilder>)
->Unit(kMillisecond);
BENCHMARK_CAPTURE(construct_proof_megahonk, keccak, &stdlib::generate_keccak_test_circuit<MegaCircuitBuilder>)
Expand All @@ -40,6 +54,11 @@ BENCHMARK_CAPTURE(construct_proof_megahonk,
&stdlib::generate_merkle_membership_test_circuit<MegaCircuitBuilder>)
->Unit(kMillisecond);

BENCHMARK(get_row_power_of_2)
// 2**15 gates to 2**20 gates
->DenseRange(15, 20)
->Unit(kMillisecond);

BENCHMARK(construct_proof_megahonk_power_of_2)
// 2**15 gates to 2**20 gates
->DenseRange(15, 20)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ template <typename FF> Polynomial<FF> sparse_random_poly(const size_t size, cons

for (size_t i = 0; i < num_nonzero; i++) {
size_t idx = engine.get_random_uint32() % size;
polynomial[idx] = FF::random_element();
polynomial.at(idx) = FF::random_element();
}

return polynomial;
Expand Down Expand Up @@ -55,7 +55,7 @@ template <typename Curve> void bench_commit_sparse(::benchmark::State& state)

auto polynomial = Polynomial<Fr>(num_points);
for (size_t i = 0; i < num_nonzero; i++) {
polynomial[i] = 1;
polynomial.at(i) = 1;
}

for (auto _ : state) {
Expand All @@ -74,7 +74,7 @@ template <typename Curve> void bench_commit_sparse_preprocessed(::benchmark::Sta

auto polynomial = Polynomial<Fr>(num_points);
for (size_t i = 0; i < num_nonzero; i++) {
polynomial[i] = 1;
polynomial.at(i) = 1;
}

for (auto _ : state) {
Expand Down Expand Up @@ -121,10 +121,7 @@ template <typename Curve> void bench_commit_random(::benchmark::State& state)
auto key = create_commitment_key<Curve>(MAX_NUM_POINTS);

const size_t num_points = 1 << state.range(0);
auto polynomial = Polynomial<Fr>(num_points);
for (auto& coeff : polynomial) {
coeff = Fr::random_element();
}
Polynomial<Fr> polynomial = Polynomial<Fr>::random(num_points);
for (auto _ : state) {
key->commit(polynomial);
}
Expand All @@ -137,15 +134,11 @@ template <typename Curve> void bench_commit_random_non_power_of_2(::benchmark::S
auto key = create_commitment_key<Curve>(MAX_NUM_POINTS);

const size_t num_points = 1 << state.range(0);
auto polynomial = Polynomial<Fr>(num_points - 1);
for (auto& coeff : polynomial) {
coeff = Fr::random_element();
}
Polynomial<Fr> polynomial = Polynomial<Fr>::random(num_points - 1);
for (auto _ : state) {
key->commit(polynomial);
}
}

BENCHMARK(bench_commit_zero<curve::BN254>)
->DenseRange(MIN_LOG_NUM_POINTS, MAX_LOG_NUM_POINTS)
->Unit(benchmark::kMillisecond);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
* simplify the codebase.
*/

#include "barretenberg/common/debug_log.hpp"
#include "barretenberg/common/op_count.hpp"
#include "barretenberg/ecc/scalar_multiplication/scalar_multiplication.hpp"
#include "barretenberg/numeric/bitop/get_msb.hpp"
#include "barretenberg/numeric/bitop/pow.hpp"
#include "barretenberg/polynomials/polynomial.hpp"
#include "barretenberg/polynomials/polynomial_arithmetic.hpp"
#include "barretenberg/srs/factories/crs_factory.hpp"
#include "barretenberg/srs/factories/file_crs_factory.hpp"
Expand Down Expand Up @@ -79,20 +81,29 @@ template <class Curve> class CommitmentKey {
* @param polynomial a univariate polynomial p(X) = ∑ᵢ aᵢ⋅Xⁱ
* @return Commitment computed as C = [p(x)] = ∑ᵢ aᵢ⋅Gᵢ
*/
Commitment commit(std::span<const Fr> polynomial)
Commitment commit(PolynomialSpan<const Fr> polynomial)
{
BB_OP_COUNT_TIME();
// See constructor, we must round up the number of used srs points to a power of 2.
const size_t consumed_srs = numeric::round_up_power_2(polynomial.size());
// We must have a power-of-2 SRS points *after* subtracting by start_index.
const size_t consumed_srs = numeric::round_up_power_2(polynomial.size()) + polynomial.start_index;
auto srs = srs::get_crs_factory<Curve>()->get_prover_crs(consumed_srs);
// We only need the
if (consumed_srs > srs->get_monomial_size()) {
info("Attempting to commit to a polynomial that needs ",
consumed_srs,
" points with an SRS of size ",
srs->get_monomial_size());
ASSERT(false);
throw_or_abort(format("Attempting to commit to a polynomial that needs ",
consumed_srs,
" points with an SRS of size ",
srs->get_monomial_size()));
}
return scalar_multiplication::pippenger_unsafe_optimized_for_non_dyadic_polys<Curve>(
polynomial, srs->get_monomial_points(), pippenger_runtime_state);

// Extract the precomputed point table (contains raw SRS points at even indices and the corresponding
// endomorphism point (\beta*x, -y) at odd indices). We offset by polynomial.start_index * 2 to align
// with our polynomial span.
std::span<G1> point_table = srs->get_monomial_points().subspan(polynomial.start_index * 2);
DEBUG_LOG_ALL(polynomial.span);
Commitment point = scalar_multiplication::pippenger_unsafe_optimized_for_non_dyadic_polys<Curve>(
polynomial.span, point_table, pippenger_runtime_state);
DEBUG_LOG(point);
return point;
};

/**
Expand All @@ -105,19 +116,20 @@ template <class Curve> class CommitmentKey {
* @param polynomial
* @return Commitment
*/
Commitment commit_sparse(std::span<const Fr> polynomial)
Commitment commit_sparse(PolynomialSpan<const Fr> polynomial)
{
BB_OP_COUNT_TIME();
const size_t degree = polynomial.size();
ASSERT(degree <= srs->get_monomial_size());
const size_t poly_size = polynomial.size();
ASSERT(polynomial.end_index() <= srs->get_monomial_size());

// Extract the precomputed point table (contains raw SRS points at even indices and the corresponding
// endomorphism point (\beta*x, -y) at odd indices).
std::span<G1> point_table = srs->get_monomial_points();
// endomorphism point (\beta*x, -y) at odd indices). We offset by polynomial.start_index * 2 to align
// with our polynomial spann.
std::span<G1> point_table = srs->get_monomial_points().subspan(polynomial.start_index * 2);

// Define structures needed to multithread the extraction of non-zero inputs
const size_t num_threads = degree >= get_num_cpus_pow2() ? get_num_cpus_pow2() : 1;
const size_t block_size = degree / num_threads;
const size_t num_threads = poly_size >= get_num_cpus_pow2() ? get_num_cpus_pow2() : 1;
const size_t block_size = poly_size / num_threads;
std::vector<std::vector<Fr>> thread_scalars(num_threads);
std::vector<std::vector<G1>> thread_points(num_threads);

Expand All @@ -128,7 +140,7 @@ template <class Curve> class CommitmentKey {

for (size_t idx = start; idx < end; ++idx) {

const Fr& scalar = polynomial[idx];
const Fr& scalar = polynomial.span[idx];

if (!scalar.is_zero()) {
thread_scalars[thread_idx].emplace_back(scalar);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,6 @@ template <typename Curve> class CommitmentTest : public ::testing::Test {

Commitment commit(const Polynomial& polynomial) { return commitment_key->commit(polynomial); }

Polynomial random_polynomial(const size_t n)
{
Polynomial p(n);
for (size_t i = 0; i < n; ++i) {
p[i] = Fr::random_element(engine);
}
return p;
}

Fr random_element() { return Fr::random_element(engine); }

OpeningPair<Curve> random_eval(const Polynomial& polynomial)
Expand All @@ -92,7 +83,7 @@ template <typename Curve> class CommitmentTest : public ::testing::Test {

std::pair<OpeningClaim<Curve>, Polynomial> random_claim(const size_t n)
{
auto polynomial = random_polynomial(n);
auto polynomial = Polynomial::random(n);
auto opening_pair = random_eval(polynomial);
auto commitment = commit(polynomial);
auto opening_claim = OpeningClaim<Curve>{ opening_pair, commitment };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@ template <class Curve> class GeminiTest : public CommitmentTest<Curve> {
using GeminiVerifier = GeminiVerifier_<Curve>;
using Fr = typename Curve::ScalarField;
using GroupElement = typename Curve::Element;
using Polynomial = typename bb::Polynomial<Fr>;

public:
void execute_gemini_and_verify_claims(size_t log_n,
std::vector<Fr> multilinear_evaluation_point,
std::vector<Fr> multilinear_evaluations,
std::vector<std::span<Fr>> multilinear_polynomials,
std::vector<std::span<Fr>> multilinear_polynomials_to_be_shifted,
std::vector<Polynomial<Fr>> multilinear_polynomials,
std::vector<Polynomial<Fr>> multilinear_polynomials_to_be_shifted,
std::vector<GroupElement> multilinear_commitments,
std::vector<GroupElement> multilinear_commitments_to_be_shifted)
{
Expand All @@ -37,8 +36,8 @@ template <class Curve> class GeminiTest : public CommitmentTest<Curve> {
batched_evaluation += multilinear_evaluations[i] * rhos[i];
}

Polynomial batched_unshifted(1 << log_n);
Polynomial batched_to_be_shifted(1 << log_n);
Polynomial<Fr> batched_unshifted(1 << log_n);
Polynomial<Fr> batched_to_be_shifted = Polynomial<Fr>::shiftable(1 << log_n);
GroupElement batched_commitment_unshifted = GroupElement::zero();
GroupElement batched_commitment_to_be_shifted = GroupElement::zero();
const size_t num_unshifted = multilinear_polynomials.size();
Expand Down Expand Up @@ -113,14 +112,14 @@ TYPED_TEST(GeminiTest, Single)
const size_t log_n = 4;

auto u = this->random_evaluation_point(log_n);
auto poly = this->random_polynomial(n);
auto poly = Polynomial<Fr>::random(n);
auto commitment = this->commit(poly);
auto eval = poly.evaluate_mle(u);

// Collect multilinear polynomials evaluations, and commitments for input to prover/verifier
std::vector<Fr> multilinear_evaluations = { eval };
std::vector<std::span<Fr>> multilinear_polynomials = { poly };
std::vector<std::span<Fr>> multilinear_polynomials_to_be_shifted = {};
std::vector<Polynomial<Fr>> multilinear_polynomials = { poly.share() };
std::vector<Polynomial<Fr>> multilinear_polynomials_to_be_shifted = {};
std::vector<GroupElement> multilinear_commitments = { commitment };
std::vector<GroupElement> multilinear_commitments_to_be_shifted = {};

Expand All @@ -144,16 +143,15 @@ TYPED_TEST(GeminiTest, SingleShift)
auto u = this->random_evaluation_point(log_n);

// shiftable polynomial must have 0 as last coefficient
auto poly = this->random_polynomial(n);
poly[0] = Fr::zero();
auto poly = Polynomial<Fr>::random(n, /*shiftable*/ 1);

auto commitment = this->commit(poly);
auto eval_shift = poly.evaluate_mle(u, true);

// Collect multilinear polynomials evaluations, and commitments for input to prover/verifier
std::vector<Fr> multilinear_evaluations = { eval_shift };
std::vector<std::span<Fr>> multilinear_polynomials = {};
std::vector<std::span<Fr>> multilinear_polynomials_to_be_shifted = { poly };
std::vector<Polynomial<Fr>> multilinear_polynomials = {};
std::vector<Polynomial<Fr>> multilinear_polynomials_to_be_shifted = { poly.share() };
std::vector<GroupElement> multilinear_commitments = {};
std::vector<GroupElement> multilinear_commitments_to_be_shifted = { commitment };

Expand All @@ -176,8 +174,8 @@ TYPED_TEST(GeminiTest, Double)

auto u = this->random_evaluation_point(log_n);

auto poly1 = this->random_polynomial(n);
auto poly2 = this->random_polynomial(n);
auto poly1 = Polynomial<Fr>::random(n);
auto poly2 = Polynomial<Fr>::random(n);

auto commitment1 = this->commit(poly1);
auto commitment2 = this->commit(poly2);
Expand All @@ -187,8 +185,8 @@ TYPED_TEST(GeminiTest, Double)

// Collect multilinear polynomials evaluations, and commitments for input to prover/verifier
std::vector<Fr> multilinear_evaluations = { eval1, eval2 };
std::vector<std::span<Fr>> multilinear_polynomials = { poly1, poly2 };
std::vector<std::span<Fr>> multilinear_polynomials_to_be_shifted = {};
std::vector<Polynomial<Fr>> multilinear_polynomials = { poly1.share(), poly2.share() };
std::vector<Polynomial<Fr>> multilinear_polynomials_to_be_shifted = {};
std::vector<GroupElement> multilinear_commitments = { commitment1, commitment2 };
std::vector<GroupElement> multilinear_commitments_to_be_shifted = {};

Expand All @@ -211,9 +209,8 @@ TYPED_TEST(GeminiTest, DoubleWithShift)

auto u = this->random_evaluation_point(log_n);

auto poly1 = this->random_polynomial(n);
auto poly2 = this->random_polynomial(n);
poly2[0] = Fr::zero(); // necessary for polynomial to be 'shiftable'
auto poly1 = Polynomial<Fr>::random(n);
auto poly2 = Polynomial<Fr>::random(n, 1); // make 'shiftable'

auto commitment1 = this->commit(poly1);
auto commitment2 = this->commit(poly2);
Expand All @@ -224,8 +221,8 @@ TYPED_TEST(GeminiTest, DoubleWithShift)

// Collect multilinear polynomials evaluations, and commitments for input to prover/verifier
std::vector<Fr> multilinear_evaluations = { eval1, eval2, eval2_shift };
std::vector<std::span<Fr>> multilinear_polynomials = { poly1, poly2 };
std::vector<std::span<Fr>> multilinear_polynomials_to_be_shifted = { poly2 };
std::vector<Polynomial<Fr>> multilinear_polynomials = { poly1.share(), poly2.share() };
std::vector<Polynomial<Fr>> multilinear_polynomials_to_be_shifted = { poly2.share() };
std::vector<GroupElement> multilinear_commitments = { commitment1, commitment2 };
std::vector<GroupElement> multilinear_commitments_to_be_shifted = { commitment2 };

Expand Down
Loading

0 comments on commit 372f23c

Please sign in to comment.