From e0c1e0b1c8cd72d8123364b06f94af1ee56834e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mamy=20Andr=C3=A9-Ratsimbazafy?= Date: Wed, 15 Apr 2020 19:38:02 +0200 Subject: [PATCH] Add EC bench on G1 + Add throughput to benches --- benchmarks/bench_ec_swei_proj_g1.nim | 58 +++++++++++++++ benchmarks/bench_elliptic_template.nim | 94 ++++++++++++++++++++++++ benchmarks/bench_fields_template.nim | 7 +- benchmarks/bench_fp.nim | 4 +- benchmarks/bench_fp12.nim | 4 +- benchmarks/bench_fp2.nim | 4 +- benchmarks/bench_fp6.nim | 4 +- constantine/arithmetic/finite_fields.nim | 2 +- 8 files changed, 167 insertions(+), 10 deletions(-) create mode 100644 benchmarks/bench_ec_swei_proj_g1.nim create mode 100644 benchmarks/bench_elliptic_template.nim diff --git a/benchmarks/bench_ec_swei_proj_g1.nim b/benchmarks/bench_ec_swei_proj_g1.nim new file mode 100644 index 0000000..a08cb6f --- /dev/null +++ b/benchmarks/bench_ec_swei_proj_g1.nim @@ -0,0 +1,58 @@ +# 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/config/curves, + ../constantine/arithmetic, + ../constantine/elliptic/ec_weierstrass_projective, + # Helpers + ../helpers/static_for, + ./bench_elliptic_template, + # Standard library + std/strutils + +# ############################################################ +# +# Benchmark of the G1 group of +# Short Weierstrass elliptic curves +# in (homogeneous) projective coordinates +# +# ############################################################ + + +const Iters = 1_000_000 +const InvIters = 1000 +const AvailableCurves = [ + # P224, + # BN254_Nogami, + BN254_Snarks, + # Curve25519, + # P256, + # Secp256k1, + # BLS12_377, + BLS12_381, + # BN446, + # FKM12_447, + # BLS12_461, + # BN462 +] + +proc main() = + separator() + staticFor i, 0, AvailableCurves.len: + const curve = AvailableCurves[i] + addBench(ECP_SWei_Proj[Fp[curve]], Iters) + separator() + +main() + +echo "Notes:" +echo " - GCC is significantly slower than Clang on multiprecision arithmetic." +echo " - The simplest operations might be optimized away by the compiler." +echo " - Fast Squaring and Fast Multiplication are possible if there are spare bits in the prime representation (i.e. the prime uses 254 bits out of 256 bits)" diff --git a/benchmarks/bench_elliptic_template.nim b/benchmarks/bench_elliptic_template.nim new file mode 100644 index 0000000..2ed2fde --- /dev/null +++ b/benchmarks/bench_elliptic_template.nim @@ -0,0 +1,94 @@ +# 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. + +# ############################################################ +# +# Benchmark of elliptic curves +# +# ############################################################ + +import + # Internals + ../constantine/config/curves, + # Helpers + ../helpers/[timers, prng_unsafe, static_for], + # Standard library + std/[monotimes, times, strformat, strutils, macros] + +var rng: RngState +let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 +rng.seed(seed) +echo "bench xoshiro512** seed: ", seed + +# warmup +proc warmup*() = + # Warmup - make sure cpu is on max perf + let start = cpuTime() + var foo = 123 + for i in 0 ..< 300_000_000: + foo += i*i mod 456 + foo = foo mod 789 + + # Compiler shouldn't optimize away the results as cpuTime rely on sideeffects + let stop = cpuTime() + echo &"Warmup: {stop - start:>4.4f} s, result {foo} (displayed to avoid compiler optimizing warmup away)\n" + +warmup() + +echo "\n⚠️ Measurements are approximate and use the CPU nominal clock: Turbo-Boost and overclocking will skew them." +echo "==========================================================================================================\n" +echo "All benchmarks are using constant-time implementations to protect against side-channel attacks." +when defined(gcc): + echo "\nCompiled with GCC" +elif defined(clang): + echo "\nCompiled with Clang" +elif defined(vcc): + echo "\nCompiled with MSVC" +elif defined(icc): + echo "\nCompiled with ICC" +else: + echo "\nCompiled with an unknown compiler" + +when defined(i386) or defined(amd64): + import ../helpers/x86 + echo "Running on ", cpuName(), "\n\n" + +proc separator*() = + echo "-".repeat(132) + +proc report(op, elliptic: string, start, stop: MonoTime, startClk, stopClk: int64, iters: int) = + let ns = inNanoseconds((stop-start) div iters) + let throughput = 1e9 / float64(ns) + echo &"{op:<15} {elliptic:<40} {throughput:>15.3f} ops/s {ns:>9} ns/op {(stopClk - startClk) div iters:>9} CPU cycles (approx)" + +macro fixEllipticDisplay(T: typedesc): untyped = + # At compile-time, enums are integers and their display is buggy + # we get the Curve ID instead of the curve name. + let instantiated = T.getTypeInst() + var name = $instantiated[1][0] # EllipticEquationFormCoordinates + let fieldName = $instantiated[1][1][0] + let curveName = $Curve(instantiated[1][1][1].intVal) + name.add "[" & fieldName & "[" & curveName & "]]" + result = newLit name + +template bench(op: string, T: typedesc, iters: int, body: untyped): untyped = + let start = getMonotime() + let startClk = getTicks() + for _ in 0 ..< iters: + body + let stopClk = getTicks() + let stop = getMonotime() + + report(op, fixEllipticDisplay(T), start, stop, startClk, stopClk, iters) + +proc addBench*(T: typedesc, iters: int) = + var r {.noInit.}: T + let x = rng.random_unsafe(T) + let y = rng.random_unsafe(T) + bench("EC Add G1", T, iters): + r.sum(x, y) diff --git a/benchmarks/bench_fields_template.nim b/benchmarks/bench_fields_template.nim index f85e597..20dbcc2 100644 --- a/benchmarks/bench_fields_template.nim +++ b/benchmarks/bench_fields_template.nim @@ -60,8 +60,13 @@ when defined(i386) or defined(amd64): import ../helpers/x86 echo "Running on ", cpuName(), "\n\n" +proc separator*() = + echo "-".repeat(107) + proc report(op, field: string, start, stop: MonoTime, startClk, stopClk: int64, iters: int) = - echo &"{op:<15} {field:<15} {inNanoseconds((stop-start) div iters):>9} ns {(stopClk - startClk) div iters:>9} cycles" + let ns = inNanoseconds((stop-start) div iters) + let throughput = 1e9 / float64(ns) + echo &"{op:<15} {field:<15} {throughput:>15.3f} ops/s {ns:>9} ns/op {(stopClk - startClk) div iters:>9} CPU cycles (approx)" macro fixFieldDisplay(T: typedesc): untyped = # At compile-time, enums are integers and their display is buggy diff --git a/benchmarks/bench_fp.nim b/benchmarks/bench_fp.nim index b3a74d0..2d2b622 100644 --- a/benchmarks/bench_fp.nim +++ b/benchmarks/bench_fp.nim @@ -41,7 +41,7 @@ const AvailableCurves = [ ] proc main() = - echo "-".repeat(80) + separator() staticFor i, 0, AvailableCurves.len: const curve = AvailableCurves[i] addBench(Fp[curve], Iters) @@ -50,7 +50,7 @@ proc main() = mulBench(Fp[curve], Iters) sqrBench(Fp[curve], Iters) invBench(Fp[curve], InvIters) - echo "-".repeat(80) + separator() main() diff --git a/benchmarks/bench_fp12.nim b/benchmarks/bench_fp12.nim index ce5fb84..f97bb56 100644 --- a/benchmarks/bench_fp12.nim +++ b/benchmarks/bench_fp12.nim @@ -38,7 +38,7 @@ const AvailableCurves = [ ] proc main() = - echo "-".repeat(80) + separator() staticFor i, 0, AvailableCurves.len: const curve = AvailableCurves[i] addBench(Fp12[curve], Iters) @@ -47,7 +47,7 @@ proc main() = mulBench(Fp12[curve], Iters) sqrBench(Fp12[curve], Iters) invBench(Fp12[curve], InvIters) - echo "-".repeat(80) + separator() main() diff --git a/benchmarks/bench_fp2.nim b/benchmarks/bench_fp2.nim index ea16f4e..ef3db2c 100644 --- a/benchmarks/bench_fp2.nim +++ b/benchmarks/bench_fp2.nim @@ -38,7 +38,7 @@ const AvailableCurves = [ ] proc main() = - echo "-".repeat(80) + separator() staticFor i, 0, AvailableCurves.len: const curve = AvailableCurves[i] addBench(Fp2[curve], Iters) @@ -47,7 +47,7 @@ proc main() = mulBench(Fp2[curve], Iters) sqrBench(Fp2[curve], Iters) invBench(Fp2[curve], InvIters) - echo "-".repeat(80) + separator() main() diff --git a/benchmarks/bench_fp6.nim b/benchmarks/bench_fp6.nim index b341dbe..06577de 100644 --- a/benchmarks/bench_fp6.nim +++ b/benchmarks/bench_fp6.nim @@ -38,7 +38,7 @@ const AvailableCurves = [ ] proc main() = - echo "-".repeat(80) + separator() staticFor i, 0, AvailableCurves.len: const curve = AvailableCurves[i] addBench(Fp6[curve], Iters) @@ -47,7 +47,7 @@ proc main() = mulBench(Fp6[curve], Iters) sqrBench(Fp6[curve], Iters) invBench(Fp6[curve], InvIters) - echo "-".repeat(80) + separator() main() diff --git a/constantine/arithmetic/finite_fields.nim b/constantine/arithmetic/finite_fields.nim index eb888ff..bafe6d2 100644 --- a/constantine/arithmetic/finite_fields.nim +++ b/constantine/arithmetic/finite_fields.nim @@ -289,7 +289,7 @@ func sqrt_if_square_p3mod4*[C](a: var Fp[C]): SecretBool = ## The square root, if it exist is multivalued, ## i.e. both x² == (-x)² ## This procedure returns a deterministic result - static: doAssert C.Mod.limbs[0].BaseType mod 4 == 3 + static: doAssert BaseType(C.Mod.limbs[0]) mod 4 == 3 var a1 {.noInit.} = a a1.powUnsafeExponent(C.getPrimeMinus3div4_BE())