Skip to content

Commit

Permalink
Benchmarks rework (#97)
Browse files Browse the repository at this point in the history
* revive Miracl primitives benchmark

* Revive BLST benchmarks

* Bench hash-to-curve

* Add benchmark of BLS sign, verify and fastAggregateVerify

* Bench all + add benchmarks to CI

* don't bench on 32-bit, inline ASM issue with low-level calls (but high level calls are fine)

* Actually it's the SHA256 tests on 32-bit that causes ASM issue due to inlined headers

* don't bench at all on 32-bit for now

* fix: don't test SH1256 on PowerPC
  • Loading branch information
mratsim authored Dec 5, 2020
1 parent 282d1f6 commit c925b0c
Show file tree
Hide file tree
Showing 8 changed files with 339 additions and 140 deletions.
21 changes: 16 additions & 5 deletions benchmarks/bench_all.nim
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
import
../blscurve,
./bls12381_curve,
./hash_to_curve
./hash_to_curve,
./bls_signature

# Curve operations
benchScalarMultG1(1000)
benchScalarMultG2(1000)
benchEcAddG1(1000)
benchEcAddG2(1000)

benchPairingViaDoublePairing(1000)
benchPairingViaMultiPairing(1000)
# Pairings
when BLS_BACKEND == BLST:
benchBLSTPairing(1000)
else:
benchMiraclPairingViaDoublePairing(1000)
benchMiraclPairingViaMultiPairing(1000)

echo "\n⚠️ Warning: using draft v5 of IETF Hash-To-Curve (HKDF-based)."
echo " This is an outdated draft.\n"
# Hash-to-curve implementation
benchHashToG2(1000)

# High-level BLS signature scheme
benchSign(1000)
benchVerify(1000)
benchFastAggregateVerify(numKeys = 128, iters = 10)
12 changes: 5 additions & 7 deletions benchmarks/bench_templates.nim
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import
# Standard library
std/[monotimes, times, strformat, strutils, macros]

from ../blscurve import BLS_BACKEND

# warmup
proc warmup*() =
# Warmup - make sure cpu is on max perf
Expand Down Expand Up @@ -41,19 +43,15 @@ else:

echo "Optimization level => no optimization: ", not defined(release), " | release: ", defined(release), " | danger: ", defined(danger)

when (sizeof(int) == 4) or defined(use32):
echo "⚠️ Warning: using Milagro with 32-bit limbs"
else:
echo "Using Milagro with 64-bit limbs"

when SupportsCPUName:
echo "Running on ", cpuName(), "\n\n"
echo "Running on ", cpuName(), "\n"

when SupportsGetTicks:
echo "\n⚠️ Cycles measurements are approximate and use the CPU nominal clock: Turbo-Boost and overclocking will skew them."
echo "i.e. a 20% overclock will be about 20% off (assuming no dynamic frequency scaling)"

echo "\n=================================================================================================================\n"
echo "\nBackend: ", $BLS_BACKEND, ", mode: ", if defined(use32): $32 else: $(sizeof(int) * 8), "-bit"
echo "=================================================================================================================\n"

proc report(op: string, start, stop: MonoTime, startClk, stopClk: int64, iters: int) =
let ns = inNanoseconds((stop-start) div iters)
Expand Down
268 changes: 199 additions & 69 deletions benchmarks/bls12381_curve.nim
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@
# those terms.

import
# Internals
../blscurve/common,
../blscurve/milagro,
../blscurve/hash_to_curve,
# Bench
./bench_templates,
./keygen
std/random,
../blscurve,
./bench_templates

when BLS_BACKEND == BLST:
import
../blscurve/blst/blst_abi
else:
import
../blscurve/miracl/[common, milagro],
../blscurve/miracl/hash_to_curve

# ############################################################
#
Expand All @@ -23,85 +27,211 @@ import
#
# ############################################################

var benchRNG = initRand(0xFACADE)

proc benchScalarMultG1*(iters: int) =
var x = generator1()
var scal: BIG_384
random(scal)
when BLS_BACKEND == BLST:
var x{.noInit.}: blst_p1
x.blst_p1_from_affine(BLS12_381_G1) # init from generator

bench("Scalar multiplication G1", iters):
x.mul(scal)
var scal{.noInit.}: array[32, byte]
for val in scal.mitems:
val = byte benchRNG.rand(0xFF)

var scalar{.noInit.}: blst_scalar
scalar.blst_scalar_from_bendian(scal)

bench("Scalar multiplication G1 (255-bit, constant-time)", iters):
x.blst_p1_mult(x, scalar, 255)
else:
var x = generator1()
var scal: BIG_384
random(scal)
scal.BIG_384_mod(CURVE_Order)

bench("Scalar multiplication G1 (255-bit, constant-time)", iters):
x.mul(scal)

proc benchScalarMultG2*(iters: int) =
var x = generator2()
var scal: BIG_384
random(scal)
when BLS_BACKEND == BLST:
var x{.noInit.}: blst_p2
x.blst_p2_from_affine(BLS12_381_G2) # init from generator

var scal{.noInit.}: array[32, byte]
for val in scal.mitems:
val = byte benchRNG.rand(0xFF)

var scalar{.noInit.}: blst_scalar
scalar.blst_scalar_from_bendian(scal)

bench("Scalar multiplication G2", iters):
x.mul(scal)
bench("Scalar multiplication G2 (255-bit, constant-time)", iters):
x.blst_p2_mult(x, scalar, 255)
else:
var x = generator2()
var scal: BIG_384
random(scal)
scal.BIG_384_mod(CURVE_Order)

bench("Scalar multiplication G2 (255-bit, constant-time)", iters):
x.mul(scal)

proc benchECAddG1*(iters: int) =
var x = generator1()
var y = generator1()
when BLS_BACKEND == BLST:
var x{.noInit.}, y{.noInit.}: blst_p1
x.blst_p1_from_affine(BLS12_381_G1) # init from generator
y = x

bench("EC add G1 (constant-time)", iters):
x.blst_p1_add_or_double(x, y)
else:
var x = generator1()
var y = generator1()

bench("EC add G1", iters):
x.add(y)
bench("EC add G1 (constant-time)", iters):
x.add(y)

proc benchECAddG2*(iters: int) =
var x = generator2()
var y = generator2()

bench("EC add G2", iters):
x.add(y)

proc benchPairingViaDoublePairing*(iters: int) =
## Builtin Milagro Double-Pairing implementation
# Ideally we don't depend on the bls_signature_scheme but it's much simpler
let (pubkey, seckey) = newKeyPair()
let msg = "msg"
const domainSepTag = "BLS_SIG_BLS12381G2-SHA256-SSWU-RO_POP_"

# Signing
var sig = hashToG2(msg, domainSepTag)
sig.mul(seckey)

# Verification
let generator = generator1()
let Q = hashToG2(msg, domainSepTag)
# Pairing: e(Q, xP) == e(R, P)
bench("Pairing (Milagro builtin double pairing)", iters):
let valid = doublePairing(
Q, pubkey,
sig, generator
when BLS_BACKEND == BLST:
var x{.noInit.}, y{.noInit.}: blst_p2
x.blst_p2_from_affine(BLS12_381_G2) # init from generator
y = x

bench("EC add G2 (constant-time)", iters):
x.blst_p2_add_or_double(x, y)
else:
var x = generator2()
var y = generator2()

bench("EC add G2 (constant-time)", iters):
x.add(y)

when BLS_BACKEND == BLST:

proc benchBLSTPairing*(iters: int) =
let (pubkey, seckey) = block:
var pk: PublicKey
var sk: SecretKey
var ikm: array[32, byte]
ikm[0] = 0x12
discard ikm.keygen(pk, sk)
(cast[blst_p1_affine](pk), cast[blst_scalar](sk))
let msg = "Mr F was here"
const domainSepTag = "BLS_SIG_BLS12381G2-SHA256-SSWU-RO_POP_"

# Signing
var sig = block:
var sig {.noInit.}: blst_p2_affine
var s {.noInit.}: blst_p2
s.blst_hash_to_g2(
msg,
domainSepTag,
aug = ""
)
s.blst_sign_pk_in_g1(s, seckey)
sig.blst_p2_to_affine(s)
sig

# Verification
let ctx = createU(blst_pairing) # Heap to avoid stack smashing
ctx[].blst_pairing_init(
hash_or_encode = kHash,
domainSepTag
)

proc benchPairingViaMultiPairing*(iters: int) =
## MultiPairing implementation
## Using deferred Miller loop + Final Exponentiation
# Ideally we don't depend on the bls_signature_scheme but it's much simpler
let (pubkey, seckey) = newKeyPair()
let msg = "msg"
const domainSepTag = "BLS_SIG_BLS12381G2-SHA256-SSWU-RO_POP_"

# Signing
var sig = hashToG2(msg, domainSepTag)
sig.mul(seckey)

# Verification
let generator = generator1()
let Q = hashToG2(msg, domainSepTag)
# Pairing: e(Q, xP) == e(R, P)
bench("Pairing (Multi-Pairing with delayed Miller and Exp)", iters):
let valid = multiPairing(
Q, pubkey,
sig, generator
doAssert BLST_SUCCESS == ctx[].blst_pairing_aggregate_pk_in_g1(
PK = pubkey.unsafeAddr,
signature = nil,
msg,
aug = ""
)
doAssert BLST_SUCCESS == ctx[].blst_pairing_aggregate_pk_in_g1(
PK = nil,
signature = sig.unsafeAddr,
msg = "",
aug = ""
)

# Cache the benchmarking context, there will be a ~8MB copy overhead (context size)
let ctxSave = createU(blst_pairing)
ctxSave[] = ctx[]

ctx[].blst_pairing_commit() # Miller loop
let valid = ctx[].blst_pairing_finalVerify(nil) # Final Exponentiation
doAssert bool valid

# Pairing: e(Q, xP) == e(R, P)
bench("Pairing (Miller loop + Final Exponentiation)", iters):
ctx[] = ctxSave[]
ctx[].blst_pairing_commit() # Miller loop
let valid = ctx[].blst_pairing_finalVerify(nil) # Final Exponentiation
# doAssert bool valid

else:

proc benchMiraclPairingViaDoublePairing*(iters: int) =
## Builtin Miracl Double-Pairing implementation
# Ideally we don't depend on the bls_signature_scheme but it's much simpler
let (pubkey, seckey) = block:
var pk: PublicKey
var sk: SecretKey
var ikm: array[32, byte]
ikm[0] = 0x12
discard ikm.keygen(pk, sk)
(cast[ECP_BLS12381](pk), cast[BIG_384](sk))
let msg = "Mr F was here"
const domainSepTag = "BLS_SIG_BLS12381G2-SHA256-SSWU-RO_POP_"

# Signing
var sig = hashToG2(msg, domainSepTag)
sig.mul(seckey)

# Verification
let generator = generator1()
let Q = hashToG2(msg, domainSepTag)
# Pairing: e(Q, xP) == e(R, P)
bench("Pairing (Milagro builtin double pairing)", iters):
let valid = doublePairing(
Q, pubkey,
sig, generator
)
# doAssert valid

proc benchMiraclPairingViaMultiPairing*(iters: int) =
## MultiPairing implementation
## Using deferred Miller loop + Final Exponentiation
# Ideally we don't depend on the bls_signature_scheme but it's much simpler
let (pubkey, seckey) = block:
var pk: PublicKey
var sk: SecretKey
var ikm: array[32, byte]
ikm[0] = 0x12
discard ikm.keygen(pk, sk)
(cast[ECP_BLS12381](pk), cast[BIG_384](sk))
let msg = "Mr F was here"
const domainSepTag = "BLS_SIG_BLS12381G2-SHA256-SSWU-RO_POP_"

# Signing
var sig = hashToG2(msg, domainSepTag)
sig.mul(seckey)

# Verification
let generator = generator1()
let Q = hashToG2(msg, domainSepTag)
# Pairing: e(Q, xP) == e(R, P)
bench("Pairing (Multi-Pairing with delayed Miller and Exp)", iters):
let valid = multiPairing(
Q, pubkey,
sig, generator
)
# doAssert valid

when isMainModule:
benchScalarMultG1(1000)
benchScalarMultG2(1000)
benchEcAddG1(1000)
benchEcAddG2(1000)

benchPairingViaDoublePairing(1000)
benchPairingViaMultiPairing(1000)
when BLS_BACKEND == BLST:
benchBLSTPairing(1000)
else:
benchMiraclPairingViaDoublePairing(1000)
benchMiraclPairingViaMultiPairing(1000)
Loading

0 comments on commit c925b0c

Please sign in to comment.