constantine/benchmarks/bench_ethereum_eip4844_kzg.nim
Mamy Ratsimbazafy 0f9b9e9606
Parallel Ethereum protocols (BLS signature and KZG) (#279)
* BLS sig: parallel batch verification

* BLS: speedup parallel batch verify with Miller loops on local threads

* shutdown bench

* nit: import style

* implement parallel KZG

* Parallel KZG commitments

* add benchmarks of KZG

* rename protocol file

* small optim: reorder await

* fix rebase

* Faster parallel BLS verification

* fix commitment status replacing previous error in verify_blob_kzg_proof_batch_parallel

* 2x faster parallel EC sum for less than 8192 points
2023-10-06 09:58:20 +02:00

231 lines
9.0 KiB
Nim

# Constantine
# Copyright (c) 2018-2019 Status Research & Development GmbH
# Copyright (c) 2020-Present Mamy André-Ratsimbazafy
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
# Internals
../constantine/ethereum_eip4844_kzg_parallel,
../constantine/math/io/io_fields,
../constantine/math/config/[curves, type_ff],
../constantine/threadpool/threadpool,
../constantine/csprngs/sysrand,
../constantine/platforms/primitives,
# Helpers
../helpers/prng_unsafe,
./bench_blueprint
proc separator*() = separator(180)
proc report(op, threads: string, startTime, stopTime: MonoTime, startClk, stopClk: int64, iters: int) =
let ns = inNanoseconds((stopTime-startTime) div iters)
let throughput = 1e9 / float64(ns)
when SupportsGetTicks:
echo &"{op:<40} {threads:<16} {throughput:>15.3f} ops/s {ns:>9} ns/op {(stopClk - startClk) div iters:>9} CPU cycles (approx)"
else:
echo &"{op:<40} {threads:<16} {throughput:>15.3f} ops/s {ns:>9} ns/op"
template bench(op, threads: string, iters: int, body: untyped): untyped =
measure(iters, startTime, stopTime, startClk, stopClk, body)
report(op, threads, startTime, stopTime, startClk, stopClk, iters)
type
BenchSet[N: static int] = ref object
blobs: array[N, Blob]
commitments: array[N, array[48, byte]]
proofs: array[N, array[48, byte]]
# This is only used for `verify_kzg_proof` and
# there is no short-circuit if they don't match
challenge, eval_at_challenge: array[32, byte]
proc new(T: type BenchSet, ctx: ptr EthereumKZGContext): T =
new(result)
for i in 0 ..< result.N:
let t {.noInit.} = rng.random_unsafe(Fr[BLS12_381])
result.blobs[i].marshal(t, bigEndian)
discard ctx.blob_to_kzg_commitment(result.commitments[i], result.blobs[i].addr)
discard ctx.compute_blob_kzg_proof(result.proofs[i], result.blobs[i].addr, result.commitments[i])
let challenge = rng.random_unsafe(Fr[BLS12_381])
let eval_at_challenge = rng.random_unsafe(Fr[BLS12_381])
discard result.challenge.marshal(challenge, bigEndian)
discard result.eval_at_challenge.marshal(eval_at_challenge, bigEndian)
proc benchBlobToKzgCommitment(b: BenchSet, ctx: ptr EthereumKZGContext, iters: int) =
let startSerial = getMonotime()
block:
bench("blob_to_kzg_commitment", "serial", iters):
var commitment {.noInit.}: array[48, byte]
doAssert cttEthKZG_Success == ctx.blob_to_kzg_commitment(commitment, b.blobs[0].addr)
let stopSerial = getMonotime()
## We require `tp` to be unintialized as even idle threads somehow reduce perf of serial benches
let tp = Threadpool.new()
let startParallel = getMonotime()
block:
bench("blob_to_kzg_commitment", $tp.numThreads & " threads", iters):
var commitment {.noInit.}: array[48, byte]
doAssert cttEthKZG_Success == tp.blob_to_kzg_commitment_parallel(ctx, commitment, b.blobs[0].addr)
let stopParallel = getMonotime()
let perfSerial = inNanoseconds((stopSerial-startSerial) div iters)
let perfParallel = inNanoseconds((stopParallel-startParallel) div iters)
let parallelSpeedup = float(perfSerial) / float(perfParallel)
echo &"Speedup ratio parallel {tp.numThreads} threads over serial: {parallelSpeedup:>6.3f}x"
proc benchComputeKzgProof(b: BenchSet, ctx: ptr EthereumKZGContext, iters: int) =
let startSerial = getMonotime()
block:
bench("compute_kzg_proof", "serial", iters):
var proof {.noInit.}: array[48, byte]
var eval_at_challenge {.noInit.}: array[32, byte]
doAssert cttEthKZG_Success == ctx.compute_kzg_proof(proof, eval_at_challenge, b.blobs[0].addr, b.challenge)
let stopSerial = getMonotime()
## We require `tp` to be unintialized as even idle threads somehow reduce perf of serial benches
let tp = Threadpool.new()
let startParallel = getMonotime()
block:
bench("compute_kzg_proof", $tp.numThreads & " threads", iters):
var proof {.noInit.}: array[48, byte]
var eval_at_challenge {.noInit.}: array[32, byte]
doAssert cttEthKZG_Success == tp.compute_kzg_proof_parallel(ctx, proof, eval_at_challenge, b.blobs[0].addr, b.challenge)
let stopParallel = getMonotime()
let perfSerial = inNanoseconds((stopSerial-startSerial) div iters)
let perfParallel = inNanoseconds((stopParallel-startParallel) div iters)
let parallelSpeedup = float(perfSerial) / float(perfParallel)
echo &"Speedup ratio parallel {tp.numThreads} threads over serial: {parallelSpeedup:>6.3f}x"
proc benchComputeBlobKzgProof(b: BenchSet, ctx: ptr EthereumKZGContext, iters: int) =
let startSerial = getMonotime()
block:
bench("compute_blob_kzg_proof", "serial", iters):
var proof {.noInit.}: array[48, byte]
doAssert cttEthKZG_Success == ctx.compute_blob_kzg_proof(proof, b.blobs[0].addr, b.commitments[0])
let stopSerial = getMonotime()
## We require `tp` to be unintialized as even idle threads somehow reduce perf of serial benches
let tp = Threadpool.new()
let startParallel = getMonotime()
block:
bench("compute_blob_kzg_proof", $tp.numThreads & " threads", iters):
var proof {.noInit.}: array[48, byte]
doAssert cttEthKZG_Success == tp.compute_blob_kzg_proof_parallel(ctx, proof, b.blobs[0].addr, b.commitments[0])
let stopParallel = getMonotime()
let perfSerial = inNanoseconds((stopSerial-startSerial) div iters)
let perfParallel = inNanoseconds((stopParallel-startParallel) div iters)
let parallelSpeedup = float(perfSerial) / float(perfParallel)
echo &"Speedup ratio parallel {tp.numThreads} threads over serial: {parallelSpeedup:>6.3f}x"
proc benchVerifyKzgProof(b: BenchSet, ctx: ptr EthereumKZGContext, iters: int) =
bench("verify_kzg_proof", "serial", iters):
discard ctx.verify_kzg_proof(b.commitments[0], b.challenge, b.eval_at_challenge, b.proofs[0])
echo "verify_kzg_proof is always serial"
proc benchVerifyBlobKzgProof(b: BenchSet, ctx: ptr EthereumKZGContext, iters: int) =
let startSerial = getMonotime()
block:
bench("verify_blob_kzg_proof", "serial", iters):
discard ctx.verify_blob_kzg_proof(b.blobs[0].addr, b.commitments[0], b.proofs[0])
let stopSerial = getMonotime()
## We require `tp` to be unintialized as even idle threads somehow reduce perf of serial benches
let tp = Threadpool.new()
let startParallel = getMonotime()
block:
bench("verify_blob_kzg_proof", $tp.numThreads & " threads", iters):
discard tp.verify_blob_kzg_proof_parallel(ctx, b.blobs[0].addr, b.commitments[0], b.proofs[0])
let stopParallel = getMonotime()
let perfSerial = inNanoseconds((stopSerial-startSerial) div iters)
let perfParallel = inNanoseconds((stopParallel-startParallel) div iters)
let parallelSpeedup = float(perfSerial) / float(perfParallel)
echo &"Speedup ratio parallel {tp.numThreads} threads over serial: {parallelSpeedup:>6.3f}x"
proc benchVerifyBlobKzgProofBatch(b: BenchSet, ctx: ptr EthereumKZGContext, iters: int) =
var secureRandomBytes {.noInit.}: array[32, byte]
discard sysrand(secureRandomBytes)
var i = 1
while i <= b.N:
let startSerial = getMonotime()
block:
bench("verify_blob_kzg_proof (batch " & $i & ')', "serial", iters):
discard verify_blob_kzg_proof_batch(
ctx,
b.blobs.asUnchecked(),
b.commitments.asUnchecked(),
b.proofs.asUnchecked(),
i,
secureRandomBytes)
let stopSerial = getMonotime()
## We require `tp` to be unintialized as even idle threads somehow reduce perf of serial benches
let tp = Threadpool.new()
let startParallel = getMonotime()
block:
bench("verify_blob_kzg_proof (batch " & $i & ')', $tp.numThreads & " threads", iters):
discard tp.verify_blob_kzg_proof_batch_parallel(
ctx,
b.blobs.asUnchecked(),
b.commitments.asUnchecked(),
b.proofs.asUnchecked(),
i,
secureRandomBytes)
let stopParallel = getMonotime()
let perfSerial = inNanoseconds((stopSerial-startSerial) div iters)
let perfParallel = inNanoseconds((stopParallel-startParallel) div iters)
let parallelSpeedup = float(perfSerial) / float(perfParallel)
echo &"Speedup ratio parallel {tp.numThreads} threads over serial: {parallelSpeedup:>6.3f}x"
echo ""
i *= 2
const Iters = 100
proc main() =
let ctx = load_ethereum_kzg_test_trusted_setup_mainnet()
let b = BenchSet[64].new(ctx)
separator()
benchBlobToKzgCommitment(b, ctx, Iters)
echo ""
benchComputeKzgProof(b, ctx, Iters)
echo ""
benchComputeBlobKzgProof(b, ctx, Iters)
echo ""
benchVerifyKzgProof(b, ctx, Iters)
echo ""
benchVerifyBlobKzgProof(b, ctx, Iters)
echo ""
benchVerifyBlobKzgProofBatch(b, ctx, Iters)
separator()
when isMainModule:
main()