From 10346d83a4817d45a819b3280cbc8f4baaa3e417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mamy=20Andr=C3=A9-Ratsimbazafy?= Date: Sun, 16 Feb 2020 01:43:17 +0100 Subject: [PATCH] Benchmark: BigInt -> Montgomery conversion: - shlAddMod (with assembly division) is already 4x slower than Montgomery Multiplication based. - constant-time division will be even slower - use montgomery-multiplication based conversion --- benchmarks/big_to_fq.nim | 48 ++++++++++++++++++++++++++++ constantine/math/bigints_checked.nim | 9 +----- constantine/math/finite_fields.nim | 6 ++-- 3 files changed, 52 insertions(+), 11 deletions(-) create mode 100644 benchmarks/big_to_fq.nim diff --git a/benchmarks/big_to_fq.nim b/benchmarks/big_to_fq.nim new file mode 100644 index 0000000..cbd1663 --- /dev/null +++ b/benchmarks/big_to_fq.nim @@ -0,0 +1,48 @@ +# 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 the conversion from Big Int to Fq +# +# ############################################################ + +# 2 implementations are possible +# - 1 based on Montgomery Multiplication +# - 1 based on modular left shift which involves multiple divisions + +import + ../constantine/config/[common, curves], + ../constantine/math/[bigints_checked, finite_fields], + random, std/monotimes, times, strformat + +const Iters = 1_000_000 + +randomize(1234) + +proc main() = + var x: BigInt[381] + x.setInternalBitLength() + for i in 0 ..< x.limbs.len - 1: + # Set x to a random value guaranteed below the prime + x.limbs[i] = Word(rand(BaseType.high.int)) + + + let start = getMonotime() + for _ in 0 ..< Iters: + let y = Fq[BLS12_381].fromBig(x) + let stop = getMonotime() + + echo &"Time for {Iters} iterations: {inMilliseconds(stop-start)} ms" + + +main() +# 1_000_000 iterations with -d:danger on i9-9980XE all-core turbo 4.1GHz +# Montgomery Multiplication based: 254ms +# shlAddMod based (using assembly div2n1n!!): 907 ms +# Note: shlAddMod will be even slower when division is made constant-time diff --git a/constantine/math/bigints_checked.nim b/constantine/math/bigints_checked.nim index 9481d35..a37e4a4 100644 --- a/constantine/math/bigints_checked.nim +++ b/constantine/math/bigints_checked.nim @@ -120,14 +120,7 @@ func unsafeMontyResidue*[mBits](mres: var BigInt[mBits], a, N, r2modN: BigInt[mB ## Caller must take care of properly switching between ## the natural and montgomery domain. ## Nesting Montgomery form is possible by applying this function twice. - # TODO: benchmark - when true: - # Montgomery multiplication based - montyResidue(mres.view, a.view, N.view, r2modN.view, Word(negInvModWord)) - else: - # Modular left shift based - mres = a - montyResidue(mres.view, N.view) + montyResidue(mres.view, a.view, N.view, r2modN.view, Word(negInvModWord)) func unsafeRedc*[mBits](mres: var BigInt[mBits], N: BigInt[mBits], negInvModWord: static BaseType) = ## Convert a BigInt from its Montgomery n-residue form diff --git a/constantine/math/finite_fields.nim b/constantine/math/finite_fields.nim index 48f4311..8f996db 100644 --- a/constantine/math/finite_fields.nim +++ b/constantine/math/finite_fields.nim @@ -49,11 +49,11 @@ debug: # # ############################################################ -func fromBig*(T: type Fq, src: BigInt): T = +func fromBig*[C: static Curve](T: type Fq[C], src: BigInt): Fq[C] {.noInit.} = ## Convert a BigInt to its Montgomery form - result.mres.unsafeMontyResidue(src, Fq.C.Mod.mres, Fq.C.getR2modP(), Fq.C.getNegInvModWord()) + result.mres.unsafeMontyResidue(src, C.Mod.mres, C.getR2modP(), C.getNegInvModWord()) -func toBig*(src: Fq): auto = +func toBig*(src: Fq): auto {.noInit.} = ## Convert a finite-field element to a BigInt in natral representation result = src.mres result.unsafeRedC(Fq.C.Mod.mres, Fq.C.getNegInvModWord())