Skip to content

Commit

Permalink
perf: improve signed value offset handling when packing scalars (PROO…
Browse files Browse the repository at this point in the history
…F-905) (#130)

# Rationale for this change
Blitzar's `packed_msm`, like `fixed_msm`, does not handle signed values.
To compute commitments with signed columns we offset signed values by
subtracting the minimum of the data type. This requires us to compute
the commitments to the minimum value of the data type and add its
commitment before pairing.

The way we currently handle the offsets is by duplicating the
`commitable_columns` and packing in 8 bit `1`s for every column with a
value. The problem with this approach is that we compute a lot of
duplicate commits.

This PR addresses this issue by condensing all the duplicate commits.
The bit table now has an 8 bit value added to handle the offset, full
commit (all `1`s), and the last commit for each commitable column.

# What changes are included in this PR?
- Duplicate offsets of all `1`s are eliminated.
- Offsets no longer double the size of the `packed_scalars` array.
- `par_iter` is removed from the the
`bit_table_and_scalars_for_packed_msm` function. Benchmarks showed it
was slowing down computation where applied.
- `is_signed` is added to the column module.
- General refactoring for improving loop efficiency.

# Are these changes tested?
Yes
  • Loading branch information
jacobtrombetta authored Sep 9, 2024
1 parent 0f965a2 commit a1cab14
Show file tree
Hide file tree
Showing 3 changed files with 851 additions and 519 deletions.
10 changes: 10 additions & 0 deletions crates/proof-of-sql/src/base/database/column.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,16 @@ impl ColumnType {
pub fn bit_size(&self) -> u32 {
self.byte_size() as u32 * 8
}

/// Returns if the column type supports signed values.
pub const fn is_signed(&self) -> bool {
match self {
Self::SmallInt | Self::Int | Self::BigInt | Self::Int128 | Self::TimestampTZ(_, _) => {
true
}
Self::Decimal75(_, _) | Self::Scalar | Self::VarChar | Self::Boolean => false,
}
}
}

/// Convert ColumnType values to some arrow DataType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,31 @@ fn compute_dory_commitments_packed_impl(
offset: usize,
setup: &DoryProverPublicSetup,
) -> Vec<DoryCommitment> {
// Make sure that the committable columns are not empty.
if committable_columns.is_empty() {
return vec![];
}

// Set the parameters.
let num_columns = 1 << setup.sigma();
let gamma_2 = &setup.prover_setup().Gamma_2;

// If the offset is larger than the number of columns, we compute an
// offset for the gamma_2 table to avoid finding sub-commits of zero.
// offset for the gamma_2 table to avoid performing msm on all zeros.
let gamma_2_offset = offset / num_columns;
let offset = offset % num_columns;

// Get the number of sub-commits for each full commit
let num_sub_commits_per_full_commit =
pack_scalars::sub_commits_per_full_commit(committable_columns, offset, num_columns);

// Get the bit table and packed scalars for the packed msm
// Pack the scalars and create the bit table.
let (bit_table, packed_scalars) = pack_scalars::bit_table_and_scalars_for_packed_msm(
committable_columns,
offset,
num_columns,
num_sub_commits_per_full_commit,
);

// Get sub commits by computing the packed msm.
let mut sub_commits_from_blitzar =
vec![ElementP2::<ark_bls12_381::g1::Config>::default(); bit_table.len()];

// Compute packed msm
if !bit_table.is_empty() {
setup.prover_setup().blitzar_packed_msm(
&mut sub_commits_from_blitzar,
Expand All @@ -49,27 +47,51 @@ fn compute_dory_commitments_packed_impl(
);
}

// Convert the sub-commits to G1Affine
// Convert the sub-commits to G1Affine.
let all_sub_commits: Vec<G1Affine> = sub_commits_from_blitzar
.par_iter()
.map(Into::into)
.collect();

// Modify the signed sub-commits by adding the offset
let modified_sub_commits = pack_scalars::modify_commits(
// Modify the sub-commits to account for signed values that were offset.
let modified_sub_commits_update = pack_scalars::modify_commits(
&all_sub_commits,
&bit_table,
committable_columns,
num_sub_commits_per_full_commit,
offset,
num_columns,
);

let gamma_2_slice = &setup.prover_setup().Gamma_2.last().unwrap()
[gamma_2_offset..gamma_2_offset + num_sub_commits_per_full_commit];
// All columns are not guaranteed to have the same number of sub-commits.
// Create a vector that stores the number of sub-commits per full commit.
let num_sub_commits_per_full_commit: Vec<usize> = committable_columns
.iter()
.map(|column| pack_scalars::num_sub_commits(column, offset, num_columns))
.collect();

// Compute the cumulative sum of the number of sub-commits per full commit.
// This is used to index into the modified sub-commits in the Doris commitment loop.
let cumulative_sub_commit_sums: Vec<usize> = num_sub_commits_per_full_commit
.iter()
.scan(0, |acc, &x| {
let prev = *acc;
*acc += x;
Some(prev)
})
.collect();

// Compute the Dory commitments using multi pairing of sub-commits
// Compute the Dory commitments using multi pairing of sub-commits.
let span = span!(Level::INFO, "multi_pairing").entered();
let dc = modified_sub_commits
.par_chunks_exact(num_sub_commits_per_full_commit)
.map(|sub_commits| DoryCommitment(pairings::multi_pairing(sub_commits, gamma_2_slice)))
let dc: Vec<DoryCommitment> = cumulative_sub_commit_sums
.par_iter()
.zip(num_sub_commits_per_full_commit.par_iter())
.map(|(&idx, &num_sub_commits)| {
let sub_commits = &modified_sub_commits_update[idx..idx + num_sub_commits];
let gamma_2_slice =
&gamma_2.last().unwrap()[gamma_2_offset..gamma_2_offset + num_sub_commits];

DoryCommitment(pairings::multi_pairing(sub_commits, gamma_2_slice))
})
.collect();
span.exit();

Expand Down
Loading

0 comments on commit a1cab14

Please sign in to comment.