Try   HackMD

Benchmarking pairing-friendly elliptic curves libraries

last benchmark update: 29/01/21

There are several pairing-friendly elliptic curves libraries used in zero-knowledge proofs (ZKP) projects. Typically, the most important elliptic curve operations in ZKP schemes are "multi scalar multiplication" (MSM) and "pairing product" (PP). This writeup is a try to benchmark and compare the building blocks of these operations in two different architechtures.
In fact, since MSM and PP are size-dependant, we focus on timings of mixed addition in G1/G2, doubling in G1/G2, Miller loop and Final exponentiation.

The libraries are written in different languages with different software and mathematical optimizations, and with more or less assembly code.

The target curves are: BN254, BLS12-381, BLS12-377 and BW6-761.

The chosen libraries are:

library benchmarked curves original authors programming language license zkp/blockahin project
gnark-crypto BN254, BLS12-381, BLS12-377, BW6-761 Consensys Go Apache-2.0 gnark
kilic/<curve> BN254, BLS12-381, BLS12-377, BW6-761 Onur Kilic Go Apache-2.0 Geth (BLS12-381)
geth/crypto/<curve> BN254, BLS12-381 go-ethereum Go LGPL-3.0 Geth
arkworks-rs/curves
(fromerly zexe)
BN254, BLS12-381, BLS12-377, BW6-761 arkworks-rs Rust* MIT/Apache-2.0 Celo, Aleo (BLS12-377, BW6-761)
celo-org/zexe
(based on arkworks)
BW6-761 celo-org Rust** MIT/Apache-2.0 Celo (BW6-761)
zkcrypto/bls12_381 BLS12-381 zkcrypto Rust MIT/Apache-2.0 zcash, Dusk
mcl BN254, BLS12-381 MITSUNARI Shigeo C++ modified BSD-3-Clause loopring (BN254), DFINITY (BLS12-381)
blst BLS12-381 Supranational C Apache-2.0 filecoin
relic BLS12-381 Diego F. Aranha C Apache-2.0/LGPL-2.1 Chia
libff
(develop branch)
BN254, BLS12-381 SCIPR Lab C++ MIT libsnark
zk-swap-libff
(based on libff)
BLS12-377, BW6-761 EYBlockchain C++ MIT zk-swap-libsnark
constantine BN254, BLS12-381, BLS12-377 Mamy Ratsimbazafy Nim MIT/Apache-2.0

* assembly is disabled by default in arkworks-rs. To enable asm in benchmarks, e.g.: RUSTFLAGS="-C target -feature=+bmi2,+adx" cargo +nightly bench bls12_381::full_pairing --features asm.

** assembly is disabled by default in celo-org/zexe. To enable asm in benchmark on BW6-761curve: RUSTFLAGS="-C target-feature=+bmi2,+adx" cargo +nightly bench pairing --features "force_bw6_asm bw6_761"

BN254

benchmark on AWS z1d.3xlarge (3.4 GHz Intel Xeon)

with hyperthreading, turbo and frequency scaling disabled.

Pairing

↓library language Full pairing (ms) Miller loop (ms) Final exponentiation (ms)
mcl (BN_SNARK1) C++ 0.4765 0.2137 0.2628
gnark-crypto(bn254) Go 0.4890 0.2321 0.2569
constantine (BN254_Snarks) Nim 0.6471 0.2934 0.3424
kilic/bn254 Go 0.8454 0.3990 0.4464
arkworks(+asm) (bn254) Rust 1.0655 0.4597 0.6058
arkworks(bn254) Rust 1.1915 0.5265 0.6649
geth/cloudflare(bn256) Go 1.3013 0.4874 0.8139
libff(alt_bn128) C++ 2.1000 0.6500 1.4500
geth/google(bn256) Go 11.4304 4.1681 7.2622

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

(geth/google is discarded in this figure as it is too slow)

G1 mixed addition/doubling

↓library language Mixed addition (ns) doubling (ns)
mcl (BN_SNARK1) C++ 216 158
gnark-crypto(bn254) Go 231 143
constantine (BN254_Snarks) Nim 237 151
arkworks(bn254) Rust 315 228
kilic/bn254 Go 413 205
geth/cloudflare(bn256) Go 472 253
libff(alt_bn128) C++ 4236 2800
geth/google(bn256) Go 8196 4102
arkworks(+asm) (bn254) Rust ^ ^
^ benchmarking arkworks group madd/double with assembly feature enabled seems to output wrong timings.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

(geth/google and libff are discarded in this figure as they are too slow)

G2 mixed addition/doubling

↓library language Mixed addition (ns) doubling (ns)
mcl (BN_SNARK1) C++ 594 374
gnark-crypto(bn254) Go 605 362
constantine (BN254_Snarks) Nim 1035 481
arkworks(bn254) Rust 1192 732
kilic/bn254 Go 1378 593
geth/cloudflare(bn256) Go 1738 732
libff(alt_bn128) C++ 6508 4533
geth/google(bn256) Go 19549 9370
arkworks(+asm) (bn254) Rust ^ ^
^ benchmarking arkworks group madd/double with assembly feature enabled seems to output wrong timings.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

(geth/google and libff are discarded in this figure as they are too slow)

benchmark on AWS c5a.2xlarge (2.7 GHz AMD EPYC 7R32)

with hyperthreading, turbo and frequency scaling disabled.

Pairing

↓library language Full pairing (ms) Miller loop (ms) Final exponentiation (ms)
gnark-crypto (bn254) Go 0.5889 0.2795 0.3094
mcl(BN_SNARK1) C++ 0.6085 0.3375 0.2710
constantine (BN254_Snarks) Nim 0.9029 0.4100 0.4772
kilic/bn254 Go 1.0362 0.5063 0.5299
arkworks(+asm) (bn254) Rust 1.3271 0.5583 0.7688
arkworks(bn254) Rust 1.3834 0.6201 0.7633
geth/cloudflare(bn256) Go 1.4720 0.5569 0.9150
libff(alt_bn128) C++ 2.800 1.300 1.500
geth/google(bn256) Go 13.3843 4.7476 8.63662

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

(geth/google is discarded in this figure as it is too slow)

G1 mixed addition/doubling

↓library language Mixed addition (ns) doubling (ns)
mcl (BN_SNARK1) C++ 264 209
gnark-crypto(bn254) Go 294 173
constantine (BN254_Snarks) Nim 349 224
arkworks(bn254) Rust 380 297
kilic/bn254 Go 495 258
geth/cloudflare(bn256) Go 514 290
arkworks(+asm) (bn254) Rust ^ ^
^ benchmarking arkworks group madd/double with assembly feature enabled seems to output wrong timings.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

(geth/google and libff are discarded in this figure as they are too slow)

G2 mixed addition/doubling

↓library language Mixed addition (ns) doubling (ns)
gnark-crypto(bn254) Go 696 425
mcl (BN_SNARK1) C++ 735 461
arkworks(bn254) Rust 1259 820
constantine (BN254_Snarks) Nim 1454 691
kilic/bn254 Go 1736 781
geth/cloudflare(bn256) Go 1912 841
arkworks(+asm) (bn254) Rust ^ ^
^ benchmarking arkworks group madd/double with assembly feature enabled seems to output wrong timings.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

(geth/google and libff are discarded in this figure as they are too slow)

BLS12-381

benchmark on AWS z1d.3xlarge (3.4 GHz Intel Xeon)

with hyperthreading, turbo and frequency scaling disabled.

Pairing

↓library language Full pairing (ms) Miller loop (ms) Final exponentiation (ms)
blst (BLS12-381) C 0.6116 0.2642 0.3474
mcl (BLS12-381) C++ 0.6794 0.2864 0.3930
gnark-crypto(bls12-381) Go 0.7078 0.3320 0.3758
kilic/bls12-381 Go 0.8594 0.3690 0.4903
constantine (BLS12_381) Nim 0.9062 0.3561 0.5210
relic (gmp-pbc-bls381 preset) C 1.3149 0.5780 0.7369
arkworks(+asm) (bls12_381) Rust 1.3271 0.5583 0.7688
geth/bls12381(bls12381, based on kilic) Go 1.3494 1.5528 0.7965
zkcrypto/bls12_381 Rust 1.5886 0.4971 1.0914
arkworks(bls12_381) Rust 1.6218 0.6931 0.9287
libff(bls12_381) C++ 3.9000 1.5000 2.4000

G1 mixed addition/doubling

↓library language Mixed addition (ns) doubling (ns)
gnark-crypto(bls12-381) Go 387 245
constantine (BLS12_381) Nim 416 269
mcl (BLS12-381) C++ 421 309
blst (BLS12-381) C 474 287
kilic/bls12-381 Go 558 282
arkworks(bls12_381) Rust 672 414
zkcrypto/bls12_381 Rust 787 565
geth/bls12381(bls12381, based on kilic) Go 789 383
relic (gmp-pbc-bls381 preset) C 938 674
libff(bls12_381) C++ 4183 3001
arkworks(+asm) (bls12_381) Rust ^ ^
^ benchmarking arkworks group madd/double with assembly feature enabled seems to output wrong timings.


(libff is discarded in this figure as it is too slow)

G2 mixed addition/doubling

↓library language Mixed addition (ns) doubling (ns)
gnark-crypto(bls12-381) Go 1123 681
mcl (BLS12-381) C++ 1164 728
blst (BLS12-381) C 1293 674
constantine (BLS12_381) Nim 1535 781
kilic/bls12-381 Go 1846 791
relic (gmp-pbc-bls381 preset) C 2126 1465
arkworks(bls12_381) Rust 2143 1302
geth/bls12381(bls12381, based on kilic) Go 2537 1052
zkcrypto/bls12_381 Rust 3071 2022
libff(bls12_381) C++ 7728 5361
arkworks(+asm) (bls12_381) Rust ^ ^
^ benchmarking arkworks group madd/double with assembly feature enabled seems to output wrong timings.


(libff is discarded in this figure as it is too slow)

benchmark on AWS c5a.2xlarge (2.7 GHz AMD EPYC 7R32)

with hyperthreading, turbo and frequency scaling disabled.

Pairing

↓library language Full pairing (ms) Miller loop (ms) Final exponentiation (ms)
blst (BLS12-381) C 0.7474 0.3230 0.4253
mcl (BLS12-381) C++ 0.8437 0.3508 0.4929
gnark-crypto(bls12-381) Go 0.8742 0.4188 0.4554
kilic/bls12-381 Go 1.0757 0.4568 0.6189
constantine (BLS12_381) Nim 1.3542 0.5425 0.7657
relic (gmp-pbc-bls381 preset) C 1.5581 0.8760 0.6821
arkworks(+asm) (bls12_381) Rust 1.5815 0.6459 0.9356
geth/bls12381(bls12381, based on kilic) Go 1.6970 0.7044 0.9926
arkworks(bls12_381) Rust 1.8994 0.8014 1.0980
zkcrypto/bls12_381 Rust 1.9324 0.7978 1.1346
libff(bls12_381) C++ 4.6000 1.7000 2.9000

G1 mixed addition/doubling

↓library language Mixed addition (ns) doubling (ns)
gnark-crypto(bls12-381) Go 511 302
mcl (BLS12-381) C++ 544 401
blst (BLS12-381) C 623 345
kilic/bls12-381 Go 683 348
constantine (BLS12_381) Nim 692 438
arkworks(bls12_381) Rust 750 547
zkcrypto/bls12_381 Rust 933 691
geth/bls12381(bls12381, based on kilic) Go 973 463
relic (gmp-pbc-bls381 preset) C 1099 803
arkworks(+asm) (bls12_381) Rust ^ ^
^ benchmarking arkworks group madd/double with assembly feature enabled seems to output wrong timings.


(libff is discarded in this figure as it is too slow)

G2 mixed addition/doubling

↓library language Mixed addition (ns) doubling (ns)
gnark-crypto(bls12-381) Go 1410 847
mcl (BLS12-381) C++ 1489 904
blst (BLS12-381) C 1621 816
kilic/bls12-381 Go 2305 1003
arkworks(bls12_381) Rust 2412 1516
relic (gmp-pbc-bls381 preset) C 2515 1750
constantine (BLS12_381) Nim 2543 1261
geth/bls12381(bls12381, based on kilic) Go 2305 1003
zkcrypto/bls12_381 Rust 3543 2344
arkworks(+asm) (bls12_381) Rust ^ ^
^ benchmarking arkworks group madd/double with assembly feature enabled seems to output wrong timings.


(libff is discarded in this figure as it is too slow)

BLS12-377

benchmark on AWS z1d.3xlarge (3.4 GHz Intel Xeon)

with hyperthreading, turbo and frequency scaling disabled.

Pairing

↓library language Full pairing (ms) Miller loop (ms) Final exponentiation (ms)
gnark-crypto (bls12-377) Go 0.9184 0.4080 0.5104
kilic/bls12-377 Go 0.9935 0.4220 0.5715
constantine(BLS12_377) Nim 1.1160 0.4430 0.6467
arkworks(+asm)(bls12_377) Rust 1.5775 0.6550 0.9225
arkworks(bls12_377) Rust 1.8769 1.3463 1.0855
EYBlockchain/libff(bls12_377) C++ 3.8000 1.4000 2.4000

G1 mixed addition/doubling

↓library language Mixed addition (ns) doubling (ns)
gnark-crypto (bls12-377) Go 389 244
constantine(BLS12_377) Nim 396 269
kilic/bls12-377 Go 557 283
arkworks(bls12_377) Rust 767 410
EYBlockchain/libff(bls12_377) C++ 4136 3071
arkworks(+asm)(bls12_377) Rust ^ ^
^ benchmarking arkworks group madd/double with assembly feature enabled seems to output wrong timings.

G2 mixed addition/doubling

↓library language Mixed addition (ns) doubling (ns)
gnark-crypto (bls12-377) Go 1424 883
constantine(BLS12_377) Nim 1877 1104
kilic/bls12-377 Go 2109 909
arkworks(bls12_377) Rust 2723 1635
EYBlockchain/libff(bls12_377) C++ 7674 5284
arkworks(+asm)(bls12_377) Rust ^ ^
^ benchmarking arkworks group madd/double with assembly feature enabled seems to output wrong timings.

benchmark on AWS c5a.2xlarge (2.7 GHz AMD EPYC 7R32)

with hyperthreading, turbo and frequency scaling disabled.

Pairing

↓library language Full pairing (ms) Miller loop (ms) Final exponentiation (ms)
gnark-crypto (bls12-377) Go 1.1381 0.5104 0.6277
kilic/bls12-377 Go 1.2539 0.5245 0.7229
constantine(BLS12_377) Nim 1.6415 0.9398 0.65337
arkworks+asm(bls12_377) Rust 1.9030 0.7747 1.1283
arkworks(bls12_377) Rust 2.2305 0.9726 1.2579
EYBlockchain/libff(bls12_377) C++ 4.5000 1.6000 2.9000

G1 mixed addition/doubling

↓library language Mixed addition (ns) doubling (ns)
gnark-crypto (bls12-377) Go 487 301
constantine(BLS12_377) Nim 664 436
kilic/bls12-377 Go 682 344
arkworks(bls12_377) Rust 761 546
arkworks(+asm)(bls12_377) Rust ^ ^
^ benchmarking arkworks group madd/double with assembly feature enabled seems to output wrong timings.


(libff is discarded in this figure as it is too slow)

G2 mixed addition/doubling

↓library language Mixed addition (ns) doubling (ns)
gnark-crypto (bls12-377) Go 1770 1082
kilic/bls12-377 Go 2669 1164
constantine(BLS12_377) Nim 2843 1632
arkworks(bls12_377) Rust 3084 1908
arkworks(+asm)(bls12_377) Rust ^ ^
^ benchmarking arkworks group madd/double with assembly feature enabled seems to output wrong timings.


(libff is discarded in this figure as it is too slow)

BW6-761

benchmark on AWS z1d.3xlarge (3.4 GHz Intel Xeon)

with hyperthreading, turbo and frequency scaling disabled.

Pairing

↓library language Full pairing (ms) Miller loop (ms) Final exponentiation (ms)
kilic/bw6 Go 2.8905 1.7953 1.0952
gnark-crypto (bw6-761) Go 3.0681 1.9453 1.1228
celo-org/zexe (bw6_761) Rust 3.6068 1.8210 1.7768
arkworks+asm (bw6_761) Rust 4.9032 2.5087 2.3946
arkworks (bw6_761) Rust 5.5040 2.8590 2.6450
EY/libff (bw6_761) C++ 6.3000 3.5000 2.8000

G1/G2 mixed addition/doubling

↓library language Mixed addition (ns) doubling (ns)
gnark-crypto (bw6-761) Go 1410 862
celo-org/zexe (bw6_761) Rust 1404 945
arkworks+asm (bw6_761) Rust 1883 1213
kilic/bw6 Go 2101 9941
arkworks (bw6_761) Rust 2193 1420
EY/libff (bw6_761) C++ 7413 4683

benchmark on AWS c5a.2xlarge (2.7 GHz AMD EPYC 7R32)

with hyperthreading, turbo and frequency scaling disabled.

Pairing

↓library language Full pairing (ms) Miller loop (ms) Final exponentiation (ms)
kilic/bw6 Go 3.4712 2.2101 1.2611
gnark-crypto (bw6-761) Go 3.5284 2.0552 1.4731
celo-org/zexe (bw6_761) Rust 4.4603 2.2589 2.2014
arkworks+asm (bw6_761) Rust 6.2773 3.2008 3.0765
arkworks (bw6_761) Rust 6.8567 3.5297 3.3270
EY/libff (bw6_761) C++ 7.6000 4.2000 3.4000

G1/G2 mixed addition/doubling

↓library language Mixed addition (ns) doubling (ns)
celo-org/zexe (bw6_761) Rust 1698 1156
gnark-crypto (bw6-761) Go 1881 1150
arkworks+asm (bw6_761) Rust 2363 1591
kilic/bw6 Go 2525 1197
arkworks (bw6_761) Rust 2666 1788


(libff is discarded in this figure as it is too slow)

Conclusion

The libraries are implemented in different languages and some use more assembly code than others. Besides the different algorithmic and software optimizations used across, it should be noted also that some libraries target constant-time implementation for some operations making it de facto slower. However, it can be clear that consensys/gnark-crypto is one of the fastest pairing-friendly elliptic curve libraries to be used in zkp projects with different curves.


Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
Author: Youssef El Housni.

I'm part of a research team at ConsenSys. If you are interested in our work (fast finite field arithmetic, elliptic curve pairings, and zero-knowledge proofs), give us a shout.