/ benchmarks / bench_fields_template.nim
bench_fields_template.nim
1 # Constantine 2 # Copyright (c) 2018-2019 Status Research & Development GmbH 3 # Copyright (c) 2020-Present Mamy André-Ratsimbazafy 4 # Licensed and distributed under either of 5 # * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). 6 # * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). 7 # at your option. This file may not be copied, modified, or distributed except according to those terms. 8 9 # ############################################################ 10 # 11 # Benchmark of finite fields 12 # 13 # ############################################################ 14 15 import 16 # Internals 17 ../constantine/platforms/abstractions, 18 ../constantine/math/config/curves, 19 ../constantine/math/arithmetic, 20 ../constantine/math/extension_fields, 21 ../constantine/math/constants/zoo_square_roots, 22 # Helpers 23 ../helpers/prng_unsafe, 24 ./bench_blueprint 25 26 export notes, abstractions 27 proc separator*() = separator(165) 28 proc smallSeparator*() = separator(8) 29 30 proc report(op, field: string, start, stop: MonoTime, startClk, stopClk: int64, iters: int) = 31 let ns = inNanoseconds((stop-start) div iters) 32 let throughput = 1e9 / float64(ns) 33 when SupportsGetTicks: 34 echo &"{op:<70} {field:<18} {throughput:>15.3f} ops/s {ns:>9} ns/op {(stopClk - startClk) div iters:>9} CPU cycles (approx)" 35 else: 36 echo &"{op:<70} {field:<18} {throughput:>15.3f} ops/s {ns:>9} ns/op" 37 38 macro fixFieldDisplay(T: typedesc): untyped = 39 # At compile-time, enums are integers and their display is buggy 40 # we get the Curve ID instead of the curve name. 41 let instantiated = T.getTypeInst() 42 var name = $instantiated[1][0] # 𝔽p 43 name.add "[" & $Curve(instantiated[1][1].intVal) & "]" 44 result = newLit name 45 46 template bench(op: string, T: typedesc, iters: int, body: untyped): untyped = 47 measure(iters, startTime, stopTime, startClk, stopClk, body) 48 report(op, fixFieldDisplay(T), startTime, stopTime, startClk, stopClk, iters) 49 50 func random_unsafe(rng: var RngState, a: var FpDbl) = 51 ## Initialize a standalone Double-Width field element 52 ## we don't reduce it modulo p², this is only used for benchmark 53 let aHi = rng.random_unsafe(Fp[FpDbl.C]) 54 let aLo = rng.random_unsafe(Fp[FpDbl.C]) 55 for i in 0 ..< aLo.mres.limbs.len: 56 a.limbs2x[i] = aLo.mres.limbs[i] 57 for i in 0 ..< aHi.mres.limbs.len: 58 a.limbs2x[aLo.mres.limbs.len+i] = aHi.mres.limbs[i] 59 60 func random_unsafe(rng: var RngState, a: var ExtensionField2x) = 61 for i in 0 ..< a.coords.len: 62 rng.random_unsafe(a.coords[i]) 63 64 proc addBench*(T: typedesc, iters: int) = 65 var x = rng.random_unsafe(T) 66 let y = rng.random_unsafe(T) 67 bench("Addition", T, iters): 68 x += y 69 70 proc subBench*(T: typedesc, iters: int) = 71 var x = rng.random_unsafe(T) 72 let y = rng.random_unsafe(T) 73 preventOptimAway(x) 74 bench("Substraction", T, iters): 75 x -= y 76 77 proc negBench*(T: typedesc, iters: int) = 78 var r: T 79 let x = rng.random_unsafe(T) 80 bench("Negation", T, iters): 81 r.neg(x) 82 83 proc ccopyBench*(T: typedesc, iters: int) = 84 var r: T 85 let x = rng.random_unsafe(T) 86 bench("Conditional Copy", T, iters): 87 r.ccopy(x, CtFalse) 88 89 proc div2Bench*(T: typedesc, iters: int) = 90 var x = rng.random_unsafe(T) 91 bench("Division by 2", T, iters): 92 x.div2() 93 94 proc mulBench*(T: typedesc, iters: int) = 95 var r: T 96 let x = rng.random_unsafe(T) 97 let y = rng.random_unsafe(T) 98 preventOptimAway(r) 99 bench("Multiplication", T, iters): 100 r.prod(x, y) 101 102 proc sqrBench*(T: typedesc, iters: int) = 103 var r: T 104 let x = rng.random_unsafe(T) 105 preventOptimAway(r) 106 bench("Squaring", T, iters): 107 r.square(x) 108 109 proc mul2xUnrBench*(T: typedesc, iters: int) = 110 var r: doublePrec(T) 111 let x = rng.random_unsafe(T) 112 let y = rng.random_unsafe(T) 113 preventOptimAway(r) 114 bench("Multiplication 2x unreduced", T, iters): 115 r.prod2x(x, y) 116 117 proc sqr2xUnrBench*(T: typedesc, iters: int) = 118 var r: doublePrec(T) 119 let x = rng.random_unsafe(T) 120 preventOptimAway(r) 121 bench("Squaring 2x unreduced", T, iters): 122 r.square2x(x) 123 124 proc rdc2xBench*(T: typedesc, iters: int) = 125 var r: T 126 var t: doublePrec(T) 127 rng.random_unsafe(t) 128 preventOptimAway(r) 129 bench("Redc 2x", T, iters): 130 r.redc2x(t) 131 132 proc sumprodBench*(T: typedesc, iters: int) = 133 var r: T 134 let a = rng.random_unsafe(T) 135 let b = rng.random_unsafe(T) 136 let u = rng.random_unsafe(T) 137 let v = rng.random_unsafe(T) 138 preventOptimAway(r) 139 bench("Linear combination", T, iters): 140 r.sumprod([a, b], [u, v]) 141 142 proc toBigBench*(T: typedesc, iters: int) = 143 var r: matchingBigInt(T.C) 144 let x = rng.random_unsafe(T) 145 preventOptimAway(r) 146 bench("BigInt <- field conversion", T, iters): 147 r.fromField(x) 148 149 proc toFieldBench*(T: typedesc, iters: int) = 150 var r: T 151 let x = rng.random_unsafe(matchingBigInt(T.C)) 152 preventOptimAway(r) 153 bench("BigInt -> field conversion", T, iters): 154 r.fromBig(x) 155 156 proc invBench*(T: typedesc, iters: int) = 157 var r: T 158 let x = rng.random_unsafe(T) 159 preventOptimAway(r) 160 bench("Inversion (constant-time)", T, iters): 161 r.inv(x) 162 163 proc invVartimeBench*(T: typedesc, iters: int) = 164 var r: T 165 let x = rng.random_unsafe(T) 166 preventOptimAway(r) 167 bench("Inversion (variable-time)", T, iters): 168 r.inv_vartime(x) 169 170 proc isSquareBench*(T: typedesc, iters: int) = 171 let x = rng.random_unsafe(T) 172 bench("isSquare (constant-time)", T, iters): 173 let qrt = x.isSquare() 174 175 proc sqrtBench*(T: typedesc, iters: int) = 176 let x = rng.random_unsafe(T) 177 178 const algoType = block: 179 when T.C.has_P_3mod4_primeModulus(): 180 "p ≡ 3 (mod 4)" 181 elif T.C.has_P_5mod8_primeModulus(): 182 "p ≡ 5 (mod 8)" 183 else: 184 "Tonelli-Shanks" 185 const addchain = block: 186 when T.C.hasSqrtAddchain() or T.C.hasTonelliShanksAddchain(): 187 "with addition chain" 188 else: 189 "without addition chain" 190 const desc = "Square Root (constant-time " & algoType & " " & addchain & ")" 191 bench(desc, T, iters): 192 var r = x 193 discard r.sqrt_if_square() 194 195 proc sqrtRatioBench*(T: typedesc, iters: int) = 196 var r: T 197 let u = rng.random_unsafe(T) 198 let v = rng.random_unsafe(T) 199 bench("Fused SquareRoot+Division+isSquare sqrt(u/v)", T, iters): 200 let isSquare = r.sqrt_ratio_if_square(u, v) 201 202 proc powBench*(T: typedesc, iters: int) = 203 let x = rng.random_unsafe(T) 204 let exponent = rng.random_unsafe(BigInt[T.C.getCurveOrderBitwidth()]) 205 bench("Exp curve order (constant-time) - " & $exponent.bits & "-bit", T, iters): 206 var r = x 207 r.pow(exponent) 208 209 proc powUnsafeBench*(T: typedesc, iters: int) = 210 let x = rng.random_unsafe(T) 211 let exponent = rng.random_unsafe(BigInt[T.C.getCurveOrderBitwidth()]) 212 bench("Exp curve order (Leak exponent bits) - " & $exponent.bits & "-bit", T, iters): 213 var r = x 214 r.pow_vartime(exponent)