Skip to main content
CompactPolynomial is a specialized polynomial type that stores coefficients as small scalars (u8, u16, u32, u64, u128, i64, i128) instead of full field elements. This optimization provides 10-100x memory reduction and significant speedups in the sumcheck inner loop.

Problem: Field Element Overhead

Jolt uses BN254’s scalar field (Fr), where each element is 32 bytes:
But many polynomials in Jolt have small integer coefficients:
  • Instruction flags: 0 or 1 (boolean)
  • Memory addresses: 0 to 2^32 - 1 (u32)
  • Register indices: 0 to 31 (u8)
  • Lookup indices: small integers

Solution: Compact Representation

CompactPolynomial<T, F> stores coefficients as small type T and lazily converts to field elements:

Memory Savings

Implementation Details

Small Scalar Trait

Location: jolt-core/src/utils/small_scalar.rs

Two-Phase Representation

CompactPolynomial has two states: 1. Initial State (coeffs only):
2. Bound State (after first bind):
After the first bind(), subsequent operations use bound_coeffs.

Optimized Binding

The first bind() converts small scalars to field elements using optimized arithmetic:

Key Optimizations

1. Lazy Conversion

Coefficients stay as small scalars until binding:

2. Optimized Arithmetic

3. Special Case Detection

For boolean polynomials (many coefficients are equal), this eliminates ~50% of multiplications.

4. Parallel Binding

Parallelization threshold: polynomials with ≥ 512 * 32 / F::NUM_BYTES elements.

Evaluation Optimizations

Inside-Out Evaluation

Faster polynomial evaluation based on Fast Polynomial Evaluation:
Speedup: ~2x vs. standard evaluation

Split EQ Evaluation

Optimized evaluation when combined with EQ polynomial:
This avoids materializing the full EQ polynomial evaluation.

Usage in Jolt

MultilinearPolynomial Dispatch

Location: jolt-core/src/poly/multilinear_polynomial.rs

Witness Polynomials

Many witness polynomials use compact representation:

Performance Impact

Sumcheck Inner Loop

The sumcheck inner loop dominates prover time:
Impact:
  • Memory bandwidth: 2-32x reduction in bytes read
  • Cache efficiency: More coefficients fit in L1/L2 cache
  • Arithmetic: Faster small-scalar multiplication

Concrete Measurements

For a program with 2^20 cycles: Prover speedup: 15-30% from reduced memory bandwidth and cache pressure.

Best Practices

When to Use CompactPolynomial

Use CompactPolynomial when:
  • Coefficients are small integers (< 2^64)
  • Polynomial is large (> 2^16 coefficients)
  • Memory is constrained
  • Polynomial is bound multiple times in sumcheck
Use DensePolynomial when:
  • Coefficients are arbitrary field elements
  • Polynomial is very small (< 2^10 coefficients)
  • Polynomial is evaluated but never bound

Choosing Scalar Type

Pick the smallest type that fits your values:

Performance Checklist

  • Use smallest scalar type that fits values
  • Pre-allocate vectors for bound_coeffs using unsafe_allocate_zero_vec
  • Avoid clones in hot paths (use references)
  • Profile to verify memory savings translate to speedup

References