---
# System prepended metadata

title: 'Technical Report: Performance Benchmark of `bn256` Implementations in the SP1 zkVM'

---

### **Technical Report: Performance Benchmark of bn256 Implementations in the SP1 zkVM**

This report details a performance comparison of different Rust libraries for `bn256` elliptic curve operations within the Succinct SP1 Zero-Knowledge Virtual Machine (zkVM). The experiment benchmarks the performance of three distinct cryptographic libraries (`substrate_bn`, `crypto-bigint`, and `arkworks`) and quantifies the impact of specialized SP1 precompiles on execution efficiency.

**Date:** August 8, 2025
**Platform:** SP1 zkVM
**Hardware:** Apple MacBook Pro (M3 Max)



### **Objective**

The primary goal of this expanded experiment is to benchmark the performance difference between:

  * The standard `substrate_bn` crate.
  * A modified `substrate_bn` that uses the `U256` type from `crypto-bigint`.
  * The `ark_bn254` crate from the `arkworks` ecosystem.

This comparison is conducted with and without SP1's specialized precompiles to measure their impact. The key performance metric is the total cycle count required to execute the guest program.



### **Methodology**

A test program simulating a tripartite Diffie-Hellman key exchange was used to create a consistent workload involving common elliptic curve operations like scalar multiplication and pairings.

#### **Guest Program Logic**

The core logic, executed inside the zkVM, performs the following steps in a loop:

1.  Generates three private keys ($sk\_A, sk\_B, sk\_C$).
2.  Calculates the corresponding public keys in groups $G\_1$ and $G\_2$ via scalar multiplication (e.g., $pk\_{A1} = G\_1 \\cdot sk\_A$).
3.  Computes a shared secret using a combination of pairing operations and exponentiation in the target group $G\_T$. For example, Alice computes her shared secret as $ss\_A = e(pk\_{B1}, pk\_{C2})^{sk\_A}$.
4.  Asserts that all three computed shared secrets are equal, verifying the correctness of the cryptography.



```rust
// General Guest Program for bn256 Key Exchange
pub fn general_guest_program() {
    let rands = init_rands_bn_batched();

    for rand in rands {
        // Generate private keys
        let alice_sk = rand;
        let bob_sk = rand + Fr::one();
        let carol_sk = bob_sk + Fr::one();

        // Generate public keys in G1 and G2
        let (alice_pk1, alice_pk2) = (G1::one() * alice_sk, G2::one() * alice_sk);
        let (bob_pk1, bob_pk2) = (G1::one() * bob_sk, G2::one() * bob_sk);
        let (carol_pk1, carol_pk2) = (G1::one() * carol_sk, G2::one() * carol_sk);

        // Each party computes the shared secret
        let alice_ss = pairing(bob_pk1, carol_pk2).pow(alice_sk);
        let bob_ss = pairing(carol_pk1, alice_pk2).pow(bob_sk);
        let carol_ss = pairing(alice_pk1, bob_pk2).pow(carol_sk);

        assert!(alice_ss == bob_ss && bob_ss == carol_ss);
    }
}
```

#### **Experimental Configurations**

Five distinct guest programs were benchmarked:

1.  **bn-pairing**: Uses the standard `substrate_bn` crate without precompile optimizations.
2.  **bigint-pairing**: Uses a modified `substrate_bn` crate that relies on `crypto-bigint` for $U256$ operations, also without precompiles.
3.  **ark-pairing**: Uses the `ark_bn254` crate from `arkworks` without precompile optimizations.
4.  **bn-pairing-patched**: Identical to `bn-pairing` but enables SP1's "fat" `bn` precompile, which accelerates the full suite of `bn256` operations.
5.  **bigint-pairing-patched**: Identical to `bigint-pairing` but enables a generic `bigint` precompile to accelerate low-level $U256$ arithmetic.
6.  **ark-pairing-patched**: This is Identical to the `ark-pairing` guest program, but the bigint operation swapped with `sp1::mul_mod` precompiles where is can be applied, this should cut do the execution cycle count. 


### **Results**

The execution cycle counts and times for each configuration are summarized below. **Lower cycle counts indicate higher performance.**

| Configuration | Base Crate | Precompile Enabled | Cycle Count | Execution Time |
| :--- | :--- | :--- | :--- | :--- |
| `bn-pairing` | `substrate_bn` | No | 1,105,498,339 | 26.8 s |
| `bigint-pairing` | `crypto-bigint` | No | 1,523,558,068 | 26.0 s |
| `ark-pairing` | `ark_bn254` | No | **428,207,591** | **7.96 s** |
| `bigint-pairing-patched` | `crypto-bigint` | Yes (`bigint`) | 518,877,400 | 11.8 s |
| `ark-pairing-patched` | `ark_bn254` | Yes (`ff-bigint`) | **422,122,898** | **2.16 s** |
| `bn-pairing-patched` | `substrate_bn` | Yes (`bn`) | **40,014,404** | **2.16 s** |


### **Analysis & Discussion**

The results provide clear insights into the performance characteristics of the libraries and the effectiveness of different optimization strategies within the zkVM.

#### **Performance Without Precompiles**

In the standard RISC-V execution environment, the choice of library has a profound impact on performance:

  * The **`ark-pairing`** program (428M cycles) was the clear winner, proving to be approximately **2.6x faster** than `bn-pairing` (1.1B cycles) and **3.5x faster** than `bigint-pairing` (1.5B cycles). This indicates that the `arkworks` implementation of `bn254` is highly optimized for this workload, even without zkVM-specific accelerations.
  * The original `substrate_bn` implementation was \~38% faster than the version modified to use `crypto-bigint`, suggesting its native integer arithmetic is more efficient for this use case.

#### **The Power of Precompiles: Specialization vs. Generalization**

The impact of precompiles is dramatic and highlights a key tradeoff between specialized and generalized acceleration.

  * **"Fat" `bn` Precompile:** Enabling the `bn` precompile (`bn-pairing-patched`) reduced the cycle count from 1.1B to just **40 million**, a staggering **\~27.6x performance improvement**. This demonstrates the immense value of a high-level, "fat" precompile that accelerates not just integer math, but the entire elliptic curve group and field logic.

  * **Generic `bigint` Precompile:** The generic `bigint` precompile (`bigint-pairing-patched`) also provided a benefit, cutting cycles from 1.5B to 519M—a **\~2.9x improvement**. However, its impact is limited because it only accelerates the fundamental $U256$ arithmetic, leaving the complex and expensive curve-specific logic to be executed as regular, less efficient instructions.

#### **Key Insight: Optimized Library vs. Generic Precompile**

The most surprising and crucial finding of this expanded research is the comparison between `ark-pairing` and `bigint-pairing-patched`.

  * The `ark-pairing` program, with **no precompiles** (428M cycles), was **\~1.2x faster** than the `bigint-pairing-patched` program (519M cycles), which **used a precompile**.

This result strongly suggests that a well-optimized, general-purpose cryptographic library (`arkworks`) can be more performant than a less-optimized library that is only aided by generic, low-level precompiles. High-level algorithmic optimizations within the library itself proved more valuable than just accelerating the underlying integer math.


### **Conclusion**

This benchmark analysis leads to two primary conclusions for developers building ZK applications on SP1:

1.  **Specialized Precompiles are Supreme:** The most effective path to performance is using specialized, high-level precompiles. The SP1 `bn` precompile delivered an order-of-magnitude performance gain that is unmatched by any other method. For applications involving `bn256`, using this precompile is essential for efficiency.

2.  **Library Choice is Critical:** In the absence of a specialized precompile, the choice of the underlying cryptographic library is paramount. A highly optimized, general-purpose library like **`arkworks`** can provide significant performance benefits, even outperforming other libraries that have been augmented with generic, low-level arithmetic precompiles.

Therefore, the recommended strategy for developers is to **prioritize specialized, high-level precompiles** whenever available. If such a precompile does not exist for a required cryptographic primitive, selecting a modern, highly-optimized library is the next best step for achieving performant and practical ZK programs.



_Report structured by an LLM (Gemini)... :)_