From 242798fb4e519edbf167b3c823589ebc69f3ec82 Mon Sep 17 00:00:00 2001 From: Mamy Ratsimbazafy Date: Wed, 17 Jul 2024 10:58:34 +0200 Subject: [PATCH] =?UTF-8?q?feat(gt-multiexp):=20add=20parallel=20multi-exp?= =?UTF-8?q?onentiation=20in=20=F0=9D=94=BE=E2=82=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- benchmarks/bench_gt_parallel_template.nim | 19 +- .../elliptic/ec_multi_scalar_mul_parallel.nim | 58 ++-- constantine/math/pairings/gt_multiexp.nim | 34 +-- .../math/pairings/gt_multiexp_parallel.nim | 258 ++++++++++++++++++ .../t_pairing_bls12_377_optate.nim | 9 +- .../t_pairing_bls12_381_gt_exp.nim | 4 +- .../t_pairing_bls12_381_gt_multiexp.nim | 2 +- .../t_pairing_bls12_381_gt_subgroup.nim | 10 +- .../t_pairing_bls12_381_optate.nim | 9 +- .../t_pairing_bn254_nogami_gt_subgroup.nim | 10 +- .../t_pairing_bn254_nogami_optate.nim | 9 +- .../t_pairing_bn254_snarks_gt_exp.nim | 4 +- .../t_pairing_bn254_snarks_gt_multiexp.nim | 2 +- .../t_pairing_bn254_snarks_gt_subgroup.nim | 10 +- .../t_pairing_bn254_snarks_optate.nim | 9 +- .../t_pairing_bw6_761_gt_subgroup.nim | 10 +- .../t_pairing_bw6_761_optate.nim | 9 +- tests/math_pairings/t_pairing_template.nim | 51 ++-- ...pairing_bls12_381_gt_multiexp_parallel.nim | 18 ++ .../parallel/t_pairing_template_parallel.nim | 88 ++++++ 20 files changed, 469 insertions(+), 154 deletions(-) create mode 100644 constantine/math/pairings/gt_multiexp_parallel.nim create mode 100644 tests/parallel/t_pairing_bls12_381_gt_multiexp_parallel.nim create mode 100644 tests/parallel/t_pairing_template_parallel.nim diff --git a/benchmarks/bench_gt_parallel_template.nim b/benchmarks/bench_gt_parallel_template.nim index c57da490..53f1c8c8 100644 --- a/benchmarks/bench_gt_parallel_template.nim +++ b/benchmarks/bench_gt_parallel_template.nim @@ -23,7 +23,7 @@ import pairings_generic, gt_exponentiations, gt_exponentiations_vartime, - gt_multiexp + gt_multiexp, gt_multiexp_parallel, ], constantine/threadpool, # Helpers @@ -125,7 +125,8 @@ proc multiExpParallelBench*[GT](ctx: var BenchMultiExpContext[GT], numInputs: in var r{.noInit.}: GT - var startNaive, stopNaive, startMultiExpBaseline, stopMultiExpBaseline, startMultiExpOpt, stopMultiExpOpt: MonoTime + var startNaive, stopNaive, startMultiExpBaseline, stopMultiExpBaseline: MonoTime + var startMultiExpOpt, stopMultiExpOpt, startMultiExpPara, stopMultiExpPara: MonoTime if numInputs <= 100000: # startNaive = getMonotime() @@ -159,9 +160,20 @@ proc multiExpParallelBench*[GT](ctx: var BenchMultiExpContext[GT], numInputs: in r.multiExp_vartime(elems, exponents) stopMultiExpOpt = getMonotime() + block: + ctx.tp = Threadpool.new() + + startMultiExpPara = getMonotime() + bench("π”Ύβ‚œ multi-exponentiations" & align($ctx.tp.numThreads & " threads", 11) & align($numInputs, 10) & " (" & $bits & "-bit exponents)", GT, iters): + ctx.tp.multiExp_vartime_parallel(r, elems, exponents) + stopMultiExpPara = getMonotime() + + ctx.tp.shutdown() + let perfNaive = inNanoseconds((stopNaive-startNaive) div iters) let perfMultiExpBaseline = inNanoseconds((stopMultiExpBaseline-startMultiExpBaseline) div iters) let perfMultiExpOpt = inNanoseconds((stopMultiExpOpt-startMultiExpOpt) div iters) + let perfMultiExpPara = inNanoseconds((stopMultiExpPara-startMultiExpPara) div iters) if numInputs <= 100000: let speedupBaseline = float(perfNaive) / float(perfMultiExpBaseline) @@ -172,3 +184,6 @@ proc multiExpParallelBench*[GT](ctx: var BenchMultiExpContext[GT], numInputs: in let speedupOptBaseline = float(perfMultiExpBaseline) / float(perfMultiExpOpt) echo &"Speedup ratio optimized over baseline linear combination: {speedupOptBaseline:>6.3f}x" + + let speedupParaOpt = float(perfMultiExpOpt) / float(perfMultiExpPara) + echo &"Speedup ratio parallel over optimized linear combination: {speedupParaOpt:>6.3f}x" diff --git a/constantine/math/elliptic/ec_multi_scalar_mul_parallel.nim b/constantine/math/elliptic/ec_multi_scalar_mul_parallel.nim index be5a5c32..0f6d6111 100644 --- a/constantine/math/elliptic/ec_multi_scalar_mul_parallel.nim +++ b/constantine/math/elliptic/ec_multi_scalar_mul_parallel.nim @@ -12,7 +12,7 @@ import constantine/named/algebras, constantine/math/endomorphisms/split_scalars, constantine/math/extension_fields, constantine/named/zoo_endomorphisms, - ../../threadpool/[threadpool, partitioners] + constantine/threadpool/[threadpool, partitioners] export bestBucketBitSize # No exceptions allowed in core cryptographic operations @@ -145,7 +145,7 @@ proc bucketAccumReduce_withInit[bits: static int, EC, ECaff]( buckets[i].setNeutral() bucketAccumReduce(windowSum[], buckets, bitIndex, miniMsmKind, c, coefs, points, N) -proc msm_vartime_parallel[bits: static int, EC, ECaff]( +proc msmImpl_vartime_parallel[bits: static int, EC, ECaff]( tp: Threadpool, r: ptr EC, coefs: ptr UncheckedArray[BigInt[bits]], points: ptr UncheckedArray[EC_aff], @@ -465,7 +465,7 @@ proc applyEndomorphism_parallel[bits: static int, ECaff]( const G = when ECaff isnot EC_ShortW_Aff: G1 else: ECaff.G - const L = ECaff.getScalarField().bits().ceilDiv_vartime(M) + 1 + const L = ECaff.getScalarField().bits().computeEndoRecodedLength(M) let splitCoefs = allocHeapArray(array[M, BigInt[L]], N) let endoBasis = allocHeapArray(array[M, ECaff], N) @@ -544,14 +544,14 @@ proc multiScalarMul_dispatch_vartime_parallel[bits: static int, F, G]( # but it has no significant impact on performance case c - of 2: withEndo(msm_vartime_parallel, tp, r, coefs, points, N, c = 2) - of 3: withEndo(msm_vartime_parallel, tp, r, coefs, points, N, c = 3) - of 4: withEndo(msm_vartime_parallel, tp, r, coefs, points, N, c = 4) - of 5: withEndo(msm_vartime_parallel, tp, r, coefs, points, N, c = 5) - of 6: withEndo(msm_vartime_parallel, tp, r, coefs, points, N, c = 6) + of 2: withEndo(msmImpl_vartime_parallel, tp, r, coefs, points, N, c = 2) + of 3: withEndo(msmImpl_vartime_parallel, tp, r, coefs, points, N, c = 3) + of 4: withEndo(msmImpl_vartime_parallel, tp, r, coefs, points, N, c = 4) + of 5: withEndo(msmImpl_vartime_parallel, tp, r, coefs, points, N, c = 5) + of 6: withEndo(msmImpl_vartime_parallel, tp, r, coefs, points, N, c = 6) - of 7: msm_vartime_parallel(tp, r, coefs, points, N, c = 7) - of 8: msm_vartime_parallel(tp, r, coefs, points, N, c = 8) + of 7: msmImpl_vartime_parallel(tp, r, coefs, points, N, c = 7) + of 8: msmImpl_vartime_parallel(tp, r, coefs, points, N, c = 8) of 9: withEndo(msmAffine_vartime_parallel_split, tp, r, coefs, points, N, c = 9, useParallelBuckets = true) of 10: withEndo(msmAffine_vartime_parallel_split, tp, r, coefs, points, N, c = 10, useParallelBuckets = true) @@ -579,23 +579,23 @@ proc multiScalarMul_dispatch_vartime_parallel[bits: static int, F]( # but it has no significant impact on performance case c - of 2: withEndo(msm_vartime_parallel, tp, r, coefs, points, N, c = 2) - of 3: withEndo(msm_vartime_parallel, tp, r, coefs, points, N, c = 3) - of 4: withEndo(msm_vartime_parallel, tp, r, coefs, points, N, c = 4) - of 5: withEndo(msm_vartime_parallel, tp, r, coefs, points, N, c = 5) - of 6: withEndo(msm_vartime_parallel, tp, r, coefs, points, N, c = 6) - - of 7: msm_vartime_parallel(tp, r, coefs, points, N, c = 7) - of 8: msm_vartime_parallel(tp, r, coefs, points, N, c = 8) - of 9: msm_vartime_parallel(tp, r, coefs, points, N, c = 9) - of 10: msm_vartime_parallel(tp, r, coefs, points, N, c = 10) - of 11: msm_vartime_parallel(tp, r, coefs, points, N, c = 11) - of 12: msm_vartime_parallel(tp, r, coefs, points, N, c = 12) - of 13: msm_vartime_parallel(tp, r, coefs, points, N, c = 13) - of 14: msm_vartime_parallel(tp, r, coefs, points, N, c = 14) - of 15: msm_vartime_parallel(tp, r, coefs, points, N, c = 16) - - of 16..17: msm_vartime_parallel(tp, r, coefs, points, N, c = 16) + of 2: withEndo(msmImpl_vartime_parallel, tp, r, coefs, points, N, c = 2) + of 3: withEndo(msmImpl_vartime_parallel, tp, r, coefs, points, N, c = 3) + of 4: withEndo(msmImpl_vartime_parallel, tp, r, coefs, points, N, c = 4) + of 5: withEndo(msmImpl_vartime_parallel, tp, r, coefs, points, N, c = 5) + of 6: withEndo(msmImpl_vartime_parallel, tp, r, coefs, points, N, c = 6) + + of 7: msmImpl_vartime_parallel(tp, r, coefs, points, N, c = 7) + of 8: msmImpl_vartime_parallel(tp, r, coefs, points, N, c = 8) + of 9: msmImpl_vartime_parallel(tp, r, coefs, points, N, c = 9) + of 10: msmImpl_vartime_parallel(tp, r, coefs, points, N, c = 10) + of 11: msmImpl_vartime_parallel(tp, r, coefs, points, N, c = 11) + of 12: msmImpl_vartime_parallel(tp, r, coefs, points, N, c = 12) + of 13: msmImpl_vartime_parallel(tp, r, coefs, points, N, c = 13) + of 14: msmImpl_vartime_parallel(tp, r, coefs, points, N, c = 14) + of 15: msmImpl_vartime_parallel(tp, r, coefs, points, N, c = 16) + + of 16..17: msmImpl_vartime_parallel(tp, r, coefs, points, N, c = 16) else: unreachable() @@ -620,7 +620,6 @@ proc multiScalarMul_vartime_parallel*[bits: static int, EC, ECaff]( ## This function can be nested in another parallel function debug: doAssert coefs.len == points.len let N = points.len - tp.multiScalarMul_dispatch_vartime_parallel(r.addr, coefs.asUnchecked(), points.asUnchecked(), N) proc multiScalarMul_vartime_parallel*[F, EC, ECaff]( @@ -631,7 +630,6 @@ proc multiScalarMul_vartime_parallel*[F, EC, ECaff]( len: int) {.meter.} = ## Multiscalar multiplication: ## r <- [aβ‚€]Pβ‚€ + [a₁]P₁ + ... + [aₙ₋₁]Pₙ₋₁ - let n = cast[int](len) let coefs_big = allocHeapArrayAligned(F.getBigInt(), n, alignment = 64) @@ -650,8 +648,6 @@ proc multiScalarMul_vartime_parallel*[EC, ECaff]( points: openArray[ECaff]) {.inline.} = ## Multiscalar multiplication: ## r <- [aβ‚€]Pβ‚€ + [a₁]P₁ + ... + [aₙ₋₁]Pₙ₋₁ - debug: doAssert coefs.len == points.len let N = points.len - tp.multiScalarMul_vartime_parallel(r.addr, coefs.asUnchecked(), points.asUnchecked(), N) diff --git a/constantine/math/pairings/gt_multiexp.nim b/constantine/math/pairings/gt_multiexp.nim index 7a25b60f..0e78fce5 100644 --- a/constantine/math/pairings/gt_multiexp.nim +++ b/constantine/math/pairings/gt_multiexp.nim @@ -6,14 +6,6 @@ # * 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. -# 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 constantine/named/algebras, constantine/math/endomorphisms/split_scalars, constantine/math/extension_fields, @@ -35,7 +27,7 @@ import constantine/named/algebras, # General utilities # ------------------------------------------------------------- -func bestBucketBitSize*(inputSize: int, scalarBitwidth: static int, useSignedBuckets, useManualTuning: static bool): int {.inline.} = +func bestBucketBitSize(inputSize: int, scalarBitwidth: static int, useSignedBuckets, useManualTuning: static bool): int {.inline.} = ## Evaluate the best bucket bit-size for the input size. ## That bucket size minimize group operations. ## This ignore cache effect. Computation can become memory-bound, especially with large buckets @@ -86,7 +78,7 @@ func bestBucketBitSize*(inputSize: int, scalarBitwidth: static int, useSignedBuc if 13 <= result: result -= 1 -func `~*=`*[Gt: ExtensionField](a: var Gt, b: Gt) {.inline.} = +func `~*=`[Gt: ExtensionField](a: var Gt, b: Gt) {.inline.} = # TODO: Analyze the inputs to see if there is avalue in more complex shortcuts (-1, or partial 0 coordinates) if a.isOne().bool(): a = b @@ -95,13 +87,13 @@ func `~*=`*[Gt: ExtensionField](a: var Gt, b: Gt) {.inline.} = else: a *= b -func `~/=`*[Gt: ExtensionField](a: var Gt, b: Gt) {.inline.} = +func `~/=`[Gt: ExtensionField](a: var Gt, b: Gt) {.inline.} = ## Cyclotomic division var t {.noInit.}: Gt t.cyclotomic_inv(b) a ~*= t -func setNeutral*[Gt: ExtensionField](a: var Gt) {.inline.} = +func setNeutral[Gt: ExtensionField](a: var Gt) {.inline.} = a.setOne() # Reference multi-exponentiation @@ -269,7 +261,7 @@ type MiniMultiExpKind* = enum kFullWindow kBottomWindow -func bucketAccumReduce*[bits: static int, GT]( +func bucketAccumReduce[bits: static int, GT]( r: var GT, buckets: ptr UncheckedArray[GT], bitIndex: int, miniMultiExpKind: static MiniMultiExpKind, c: static int, @@ -371,8 +363,8 @@ proc applyEndomorphism[bits: static int, GT]( expos: ptr UncheckedArray[BigInt[bits]], N: int): auto = ## Decompose (elems, expos) into mini-scalars - ## Returns a new triplet (endoElems, endoexpos, N) - ## endoElems and endoexpos MUST be freed afterwards + ## Returns a new triplet (endoElems, endoExpos, N) + ## endoElems and endoExpos MUST be freed afterwards const M = when Gt is Fp6: 2 elif Gt is Fp12: 4 @@ -383,16 +375,16 @@ proc applyEndomorphism[bits: static int, GT]( let endoBasis = allocHeapArray(array[M, GT], N) for i in 0 ..< N: - var negatePoints {.noinit.}: array[M, SecretBool] - splitExpos[i].decomposeEndo(negatePoints, expos[i], Fr[Gt.Name].bits(), Gt.Name, G2) # π”Ύβ‚œ has same decomposition as 𝔾₂ - if negatePoints[0].bool: + var negateElems {.noinit.}: array[M, SecretBool] + splitExpos[i].decomposeEndo(negateElems, expos[i], Fr[Gt.Name].bits(), Gt.Name, G2) # π”Ύβ‚œ has same decomposition as 𝔾₂ + if negateElems[0].bool: endoBasis[i][0].cyclotomic_inv(elems[i]) else: endoBasis[i][0] = elems[i] cast[ptr array[M-1, GT]](endoBasis[i][1].addr)[].computeEndomorphisms(elems[i]) for m in 1 ..< M: - if negatePoints[m].bool: + if negateElems[m].bool: endoBasis[i][m].cyclotomic_inv() let endoElems = cast[ptr UncheckedArray[GT]](endoBasis) @@ -457,7 +449,7 @@ func multiExp_vartime*[bits: static int, GT]( r: var GT, elems: ptr UncheckedArray[GT], expos: ptr UncheckedArray[BigInt[bits]], - len: int) {.tags:[VarTime, Alloca, HeapAlloc], meter.} = + len: int) {.tags:[VarTime, Alloca, HeapAlloc], meter, inline.} = ## Multiexponentiation: ## r <- gβ‚€^aβ‚€ + g₁^a₁ + ... + gβ‚™^aβ‚™ multiExp_dispatch_vartime(r, elems, expos, len) @@ -465,7 +457,7 @@ func multiExp_vartime*[bits: static int, GT]( func multiExp_vartime*[bits: static int, GT]( r: var GT, elems: openArray[GT], - expos: openArray[BigInt[bits]]) {.tags:[VarTime, Alloca, HeapAlloc], meter.} = + expos: openArray[BigInt[bits]]) {.tags:[VarTime, Alloca, HeapAlloc], meter, inline.} = ## Multiexponentiation: ## r <- gβ‚€^aβ‚€ + g₁^a₁ + ... + gβ‚™^aβ‚™ debug: doAssert elems.len == expos.len diff --git a/constantine/math/pairings/gt_multiexp_parallel.nim b/constantine/math/pairings/gt_multiexp_parallel.nim new file mode 100644 index 00000000..cb81c613 --- /dev/null +++ b/constantine/math/pairings/gt_multiexp_parallel.nim @@ -0,0 +1,258 @@ +# 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 constantine/named/algebras, + constantine/math/endomorphisms/split_scalars, + constantine/math/extension_fields, + constantine/math/arithmetic, + constantine/named/zoo_endomorphisms, + constantine/platforms/abstractions, + ./cyclotomic_subgroups, + constantine/threadpool + +import ./gt_multiexp {.all.} + +# No exceptions allowed in core cryptographic operations +{.push raises: [].} +{.push checks: off.} + +# ########################################################### # +# # +# Multi-Exponentiation in π”Ύβ‚œ # +# # +# ########################################################### # + +proc bucketAccumReduce_withInit[bits: static int, GT]( + windowProd: ptr GT, + buckets: ptr GT or ptr UncheckedArray[GT], + bitIndex: int, miniMultiExpKind: static MiniMultiExpKind, c: static int, + elems: ptr UncheckedArray[GT], expos: ptr UncheckedArray[BigInt[bits]], N: int) = + const numBuckets = 1 shl (c-1) + let buckets = cast[ptr UncheckedArray[GT]](buckets) + for i in 0 ..< numBuckets: + buckets[i].setNeutral() + bucketAccumReduce(windowProd[], buckets, bitIndex, miniMultiExpKind, c, elems, expos, N) + +proc multiexpImpl_vartime_parallel[bits: static int, GT]( + tp: Threadpool, + r: ptr GT, + elems: ptr UncheckedArray[GT], expos: ptr UncheckedArray[BigInt[bits]], + N: int, c: static int) = + + # Prologue + # -------- + const numBuckets = 1 shl (c-1) + const numFullWindows = bits div c + const numWindows = numFullWindows + 1 # Even if `bits div c` is exact, the signed recoding needs to see an extra 0 after the MSB + + # Instead of storing the result in futures, risking them being scattered in memory + # we store them in a contiguous array, and the synchronizing future just returns a bool. + # top window is done on this thread + let miniMultiExpsResults = allocHeapArray(GT, numFullWindows) + let miniMultiExpsReady = allocStackArray(FlowVar[bool], numFullWindows) + + let bucketsMatrix = allocHeapArray(GT, numBuckets*numWindows) + + # Algorithm + # --------- + + block: # 1. Bucket accumulation and reduction + miniMultiExpsReady[0] = tp.spawnAwaitable bucketAccumReduce_withInit( + miniMultiExpsResults[0].addr, + bucketsMatrix[0].addr, + bitIndex = 0, kBottomWindow, c, + elems, expos, N) + + for w in 1 ..< numFullWindows: + miniMultiExpsReady[w] = tp.spawnAwaitable bucketAccumReduce_withInit( + miniMultiExpsResults[w].addr, + bucketsMatrix[w*numBuckets].addr, + bitIndex = w*c, kFullWindow, c, + elems, expos, N) + + # Last window is done sync on this thread, directly initializing r + const excess = bits mod c + const top = bits-excess + + when top != 0: + when excess != 0: + bucketAccumReduce_withInit( + r, + bucketsMatrix[numFullWindows*numBuckets].addr, + bitIndex = top, kTopWindow, c, + elems, expos, N) + else: + r[].setNeutral() + + # 3. Final reduction, r initialized to what would be miniMSMsReady[numWindows-1] + when excess != 0: + for w in countdown(numWindows-2, 0): + for _ in 0 ..< c: + r[].cyclotomic_square() + discard sync miniMultiExpsReady[w] + r[] ~*= miniMultiExpsResults[w] + elif numWindows >= 2: + discard sync miniMultiExpsReady[numWindows-2] + r[] = miniMultiExpsResults[numWindows-2] + for w in countdown(numWindows-3, 0): + for _ in 0 ..< c: + r[].cyclotomic_square() + discard sync miniMultiExpsReady[w] + r[] ~*= miniMultiExpsResults[w] + + # Cleanup + # ------- + miniMultiExpsResults.freeHeap() + bucketsMatrix.freeHeap() + +# Endomorphism acceleration +# ----------------------------------------------------------------------------------------------------------------------- + +proc applyEndomorphism_parallel[bits: static int, GT]( + tp: Threadpool, + elems: ptr UncheckedArray[GT], + expos: ptr UncheckedArray[BigInt[bits]], + N: int): auto = + ## Decompose (elems, expos) into mini-scalars + ## Returns a new triplet (endoElems, endoExpos, N) + ## endoElems and endoExpos MUST be freed afterwards + + const M = when Gt is Fp6: 2 + elif Gt is Fp12: 4 + else: {.error: "Unconfigured".} + + const L = Fr[Gt.Name].bits().computeEndoRecodedLength(M) + let splitExpos = allocHeapArray(array[M, BigInt[L]], N) + let endoBasis = allocHeapArray(array[M, GT], N) + + syncScope: + tp.parallelFor i in 0 ..< N: + captures: {elems, expos, splitExpos, endoBasis} + + var negateElems {.noinit.}: array[M, SecretBool] + splitExpos[i].decomposeEndo(negateElems, expos[i], Fr[Gt.Name].bits(), Gt.Name, G2) # π”Ύβ‚œ has same decomposition as 𝔾₂ + if negateElems[0].bool: + endoBasis[i][0].cyclotomic_inv(elems[i]) + else: + endoBasis[i][0] = elems[i] + + cast[ptr array[M-1, GT]](endoBasis[i][1].addr)[].computeEndomorphisms(elems[i]) + for m in 1 ..< M: + if negateElems[m].bool: + endoBasis[i][m].cyclotomic_inv() + + let endoElems = cast[ptr UncheckedArray[GT]](endoBasis) + let endoExpos = cast[ptr UncheckedArray[BigInt[L]]](splitExpos) + + return (endoElems, endoExpos, M*N) + +template withEndo[exponentsBits: static int, GT]( + multiExpProc: untyped, + tp: Threadpool, + r: ptr GT, + elems: ptr UncheckedArray[GT], + expos: ptr UncheckedArray[BigInt[exponentsBits]], + N: int, c: static int) = + when Gt.Name.hasEndomorphismAcceleration() and + EndomorphismThreshold <= exponentsBits and + exponentsBits <= Fr[Gt.Name].bits(): + let (endoElems, endoExpos, endoN) = applyEndomorphism_parallel(tp, elems, expos, N) + # Given that bits and N changed, we are able to use a bigger `c` + # TODO: bench + multiExpProc(tp, r, endoElems, endoExpos, endoN, c) + freeHeap(endoElems) + freeHeap(endoExpos) + else: + multiExpProc(tp, r, elems, expos, N, c) + +# Algorithm selection +# ----------------------------------------------------------------------------------------------------------------------- + +proc multiexp_dispatch_vartime_parallel[bits: static int, GT]( + tp: Threadpool, + r: ptr GT, + elems: ptr UncheckedArray[GT], + expos: ptr UncheckedArray[BigInt[bits]], N: int) = + ## Multiexponentiation: + ## r <- gβ‚€^aβ‚€ + g₁^a₁ + ... + gβ‚™^aβ‚™ + let c = bestBucketBitSize(N, bits, useSignedBuckets = true, useManualTuning = true) + + # Given that bits and N change after applying an endomorphism, + # we are able to use a bigger `c` + # TODO: benchmark + + case c + of 2: withEndo(multiExpImpl_vartime_parallel, tp, r, elems, expos, N, c = 2) + of 3: withEndo(multiExpImpl_vartime_parallel, tp, r, elems, expos, N, c = 3) + of 4: withEndo(multiExpImpl_vartime_parallel, tp, r, elems, expos, N, c = 4) + of 5: withEndo(multiExpImpl_vartime_parallel, tp, r, elems, expos, N, c = 5) + of 6: withEndo(multiExpImpl_vartime_parallel, tp, r, elems, expos, N, c = 6) + of 7: withEndo(multiExpImpl_vartime_parallel, tp, r, elems, expos, N, c = 7) + of 8: withEndo(multiExpImpl_vartime_parallel, tp, r, elems, expos, N, c = 8) + of 9: withEndo(multiExpImpl_vartime_parallel, tp, r, elems, expos, N, c = 9) + of 10: withEndo(multiExpImpl_vartime_parallel, tp, r, elems, expos, N, c = 10) + of 11: withEndo(multiExpImpl_vartime_parallel, tp, r, elems, expos, N, c = 11) + of 12: withEndo(multiExpImpl_vartime_parallel, tp, r, elems, expos, N, c = 12) + of 13: withEndo(multiExpImpl_vartime_parallel, tp, r, elems, expos, N, c = 13) + of 14: multiExpImpl_vartime_parallel(tp, r, elems, expos, N, c = 14) + of 15: multiExpImpl_vartime_parallel(tp, r, elems, expos, N, c = 15) + + of 16..17: multiExpImpl_vartime_parallel(tp, r, elems, expos, N, c = 16) + else: + unreachable() + +proc multiExp_vartime_parallel*[bits: static int, GT]( + tp: Threadpool, + r: ptr GT, + elems: ptr UncheckedArray[GT], + expos: ptr UncheckedArray[BigInt[bits]], + len: int) {.meter, inline.} = + ## Multiexponentiation: + ## r <- gβ‚€^aβ‚€ + g₁^a₁ + ... + gβ‚™^aβ‚™ + tp.multiExp_dispatch_vartime_parallel(r, elems, expos, len) + +proc multiExp_vartime_parallel*[bits: static int, GT]( + tp: Threadpool, + r: var GT, + elems: openArray[GT], + expos: openArray[BigInt[bits]]) {.meter, inline.} = + ## Multiexponentiation: + ## r <- gβ‚€^aβ‚€ + g₁^a₁ + ... + gβ‚™^aβ‚™ + debug: doAssert elems.len == expos.len + let N = elems.len + tp.multiExp_dispatch_vartime_parallel(r.addr, elems.asUnchecked(), expos.asUnchecked(), N) + +proc multiExp_vartime_parallel*[F, GT]( + tp: Threadpool, + r: ptr GT, + elems: ptr UncheckedArray[GT], + expos: ptr UncheckedArray[F], + len: int) {.meter.} = + ## Multiexponentiation: + ## r <- gβ‚€^aβ‚€ + g₁^a₁ + ... + gβ‚™^aβ‚™ + let n = cast[int](len) + let expos_big = allocHeapArrayAligned(F.getBigInt(), n, alignment = 64) + + syncScope: + tp.parallelFor i in 0 ..< n: + captures: {expos, expos_big} + expos_big[i].fromField(expos[i]) + tp.multiExp_vartime_parallel(r, elems, expos_big, n) + + freeHeapAligned(expos_big) + +proc multiExp_vartime_parallel*[GT]( + tp: Threadpool, + r: var GT, + elems: openArray[GT], + expos: openArray[Fr]) {.meter, inline.} = + ## Multiexponentiation: + ## r <- gβ‚€^aβ‚€ + g₁^a₁ + ... + gβ‚™^aβ‚™ + debug: doAssert elems.len == expos.len + let N = elems.len + tp.multiExp_vartime_parallel(r.addr, elems.asUnchecked(), expos.asUnchecked(), N) diff --git a/tests/math_pairings/t_pairing_bls12_377_optate.nim b/tests/math_pairings/t_pairing_bls12_377_optate.nim index d6cbe09e..87f2cbd7 100644 --- a/tests/math_pairings/t_pairing_bls12_377_optate.nim +++ b/tests/math_pairings/t_pairing_bls12_377_optate.nim @@ -6,14 +6,11 @@ # * 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 - constantine/math/pairings/pairings_bls12, - # Test utilities - ./t_pairing_template +import ./t_pairing_template runPairingTests( - 4, BLS12_377, + BLS12_377, G1 = EC_ShortW_Prj[Fp[BLS12_377], G1], G2 = EC_ShortW_Prj[Fp2[BLS12_377], G2], GT = Fp12[BLS12_377], - pairing_bls12) + iters = 4) diff --git a/tests/math_pairings/t_pairing_bls12_381_gt_exp.nim b/tests/math_pairings/t_pairing_bls12_381_gt_exp.nim index 2309c628..9f65921d 100644 --- a/tests/math_pairings/t_pairing_bls12_381_gt_exp.nim +++ b/tests/math_pairings/t_pairing_bls12_381_gt_exp.nim @@ -10,6 +10,4 @@ import # Test utilities ./t_pairing_template -runGTexponentiationTests( - Iters = 4, - GT = Fp12[BLS12_381]) +runGTexponentiationTests(GT = Fp12[BLS12_381], iters =4) diff --git a/tests/math_pairings/t_pairing_bls12_381_gt_multiexp.nim b/tests/math_pairings/t_pairing_bls12_381_gt_multiexp.nim index e9a838f1..3a95462c 100644 --- a/tests/math_pairings/t_pairing_bls12_381_gt_multiexp.nim +++ b/tests/math_pairings/t_pairing_bls12_381_gt_multiexp.nim @@ -15,4 +15,4 @@ const numPoints = [1, 2, 8, 16, 128, 256, 1024] runGTmultiexpTests( GT = Fp12[BLS12_381], numPoints, - Iters = 4) + iters = 4) diff --git a/tests/math_pairings/t_pairing_bls12_381_gt_subgroup.nim b/tests/math_pairings/t_pairing_bls12_381_gt_subgroup.nim index 371ef622..31ea0f87 100644 --- a/tests/math_pairings/t_pairing_bls12_381_gt_subgroup.nim +++ b/tests/math_pairings/t_pairing_bls12_381_gt_subgroup.nim @@ -6,12 +6,6 @@ # * 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 - constantine/math/pairings/pairings_bls12, - # Test utilities - ./t_pairing_template +import ./t_pairing_template -runGTsubgroupTests( - Iters = 4, - GT = Fp12[BLS12_381], - finalExpHard_BLS12) +runGTsubgroupTests(GT = Fp12[BLS12_381], iters = 4) diff --git a/tests/math_pairings/t_pairing_bls12_381_optate.nim b/tests/math_pairings/t_pairing_bls12_381_optate.nim index eda18b2e..b75fa67b 100644 --- a/tests/math_pairings/t_pairing_bls12_381_optate.nim +++ b/tests/math_pairings/t_pairing_bls12_381_optate.nim @@ -6,14 +6,11 @@ # * 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 - constantine/math/pairings/pairings_bls12, - # Test utilities - ./t_pairing_template +import ./t_pairing_template runPairingTests( - 4, BLS12_381, + BLS12_381, G1 = EC_ShortW_Prj[Fp[BLS12_381], G1], G2 = EC_ShortW_Prj[Fp2[BLS12_381], G2], GT = Fp12[BLS12_381], - pairing_bls12) + iters = 4) diff --git a/tests/math_pairings/t_pairing_bn254_nogami_gt_subgroup.nim b/tests/math_pairings/t_pairing_bn254_nogami_gt_subgroup.nim index 0838afd0..71b9a8ad 100644 --- a/tests/math_pairings/t_pairing_bn254_nogami_gt_subgroup.nim +++ b/tests/math_pairings/t_pairing_bn254_nogami_gt_subgroup.nim @@ -6,12 +6,6 @@ # * 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 - constantine/math/pairings/pairings_bn, - # Test utilities - ./t_pairing_template +import ./t_pairing_template -runGTsubgroupTests( - Iters = 4, - GT = Fp12[BN254_Nogami], - finalExpHard_BN) +runGTsubgroupTests(GT = Fp12[BN254_Nogami], iters = 4) diff --git a/tests/math_pairings/t_pairing_bn254_nogami_optate.nim b/tests/math_pairings/t_pairing_bn254_nogami_optate.nim index 6c72eea5..d5b3fc75 100644 --- a/tests/math_pairings/t_pairing_bn254_nogami_optate.nim +++ b/tests/math_pairings/t_pairing_bn254_nogami_optate.nim @@ -6,14 +6,11 @@ # * 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 - constantine/math/pairings/pairings_bn, - # Test utilities - ./t_pairing_template +import ./t_pairing_template runPairingTests( - 4, BN254_Nogami, + BN254_Nogami, G1 = EC_ShortW_Prj[Fp[BN254_Nogami], G1], G2 = EC_ShortW_Prj[Fp2[BN254_Nogami], G2], GT = Fp12[BN254_Nogami], - pairing_bn) + iters = 4) diff --git a/tests/math_pairings/t_pairing_bn254_snarks_gt_exp.nim b/tests/math_pairings/t_pairing_bn254_snarks_gt_exp.nim index dea4efd8..38d8cc3f 100644 --- a/tests/math_pairings/t_pairing_bn254_snarks_gt_exp.nim +++ b/tests/math_pairings/t_pairing_bn254_snarks_gt_exp.nim @@ -10,6 +10,4 @@ import # Test utilities ./t_pairing_template -runGTexponentiationTests( - Iters = 4, - GT = Fp12[BN254_Nogami]) +runGTexponentiationTests(GT = Fp12[BN254_Nogami], iters = 4) diff --git a/tests/math_pairings/t_pairing_bn254_snarks_gt_multiexp.nim b/tests/math_pairings/t_pairing_bn254_snarks_gt_multiexp.nim index cda9c5b4..d9e746a9 100644 --- a/tests/math_pairings/t_pairing_bn254_snarks_gt_multiexp.nim +++ b/tests/math_pairings/t_pairing_bn254_snarks_gt_multiexp.nim @@ -15,4 +15,4 @@ const numPoints = [1, 2, 8, 16, 128, 256, 1024] runGTmultiexpTests( GT = Fp12[BN254_Snarks], numPoints, - Iters = 4) + iters = 4) diff --git a/tests/math_pairings/t_pairing_bn254_snarks_gt_subgroup.nim b/tests/math_pairings/t_pairing_bn254_snarks_gt_subgroup.nim index 7a3002de..d7879779 100644 --- a/tests/math_pairings/t_pairing_bn254_snarks_gt_subgroup.nim +++ b/tests/math_pairings/t_pairing_bn254_snarks_gt_subgroup.nim @@ -6,12 +6,6 @@ # * 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 - constantine/math/pairings/pairings_bn, - # Test utilities - ./t_pairing_template +import ./t_pairing_template -runGTsubgroupTests( - Iters = 4, - GT = Fp12[BN254_Snarks], - finalExpHard_BN) +runGTsubgroupTests(GT = Fp12[BN254_Snarks], iters = 4) diff --git a/tests/math_pairings/t_pairing_bn254_snarks_optate.nim b/tests/math_pairings/t_pairing_bn254_snarks_optate.nim index 59635f76..6cbd21ea 100644 --- a/tests/math_pairings/t_pairing_bn254_snarks_optate.nim +++ b/tests/math_pairings/t_pairing_bn254_snarks_optate.nim @@ -6,14 +6,11 @@ # * 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 - constantine/math/pairings/pairings_bn, - # Test utilities - ./t_pairing_template +import ./t_pairing_template runPairingTests( - 4, BN254_Snarks, + BN254_Snarks, G1 = EC_ShortW_Prj[Fp[BN254_Snarks], G1], G2 = EC_ShortW_Prj[Fp2[BN254_Snarks], G2], GT = Fp12[BN254_Snarks], - pairing_bn) + iters = 4) diff --git a/tests/math_pairings/t_pairing_bw6_761_gt_subgroup.nim b/tests/math_pairings/t_pairing_bw6_761_gt_subgroup.nim index 7be0753b..4a5e72c1 100644 --- a/tests/math_pairings/t_pairing_bw6_761_gt_subgroup.nim +++ b/tests/math_pairings/t_pairing_bw6_761_gt_subgroup.nim @@ -6,12 +6,6 @@ # * 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 - constantine/math/pairings/pairings_bw6_761, - # Test utilities - ./t_pairing_template +import ./t_pairing_template -runGTsubgroupTests( - Iters = 4, - GT = Fp6[BW6_761], - finalExpHard_BW6_761) +runGTsubgroupTests(GT = Fp6[BW6_761], iters = 4) diff --git a/tests/math_pairings/t_pairing_bw6_761_optate.nim b/tests/math_pairings/t_pairing_bw6_761_optate.nim index 34870bed..7c92c115 100644 --- a/tests/math_pairings/t_pairing_bw6_761_optate.nim +++ b/tests/math_pairings/t_pairing_bw6_761_optate.nim @@ -6,14 +6,11 @@ # * 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 - constantine/math/pairings/pairings_bw6_761, - # Test utilities - ./t_pairing_template +import ./t_pairing_template runPairingTests( - 4, BW6_761, + BW6_761, G1 = EC_ShortW_Prj[Fp[BW6_761], G1], G2 = EC_ShortW_Prj[Fp[BW6_761], G2], GT = Fp6[BW6_761], - pairing_bw6_761_reference) + iters = 4) diff --git a/tests/math_pairings/t_pairing_template.nim b/tests/math_pairings/t_pairing_template.nim index 10c7debb..a19b777b 100644 --- a/tests/math_pairings/t_pairing_template.nim +++ b/tests/math_pairings/t_pairing_template.nim @@ -14,7 +14,7 @@ import constantine/math/arithmetic, constantine/math/extension_fields, constantine/named/algebras, - constantine/named/zoo_subgroups, + constantine/named/[zoo_subgroups, zoo_pairings], constantine/math/elliptic/[ec_shortweierstrass_affine, ec_shortweierstrass_projective], constantine/math/pairings/[ cyclotomic_subgroups, @@ -28,13 +28,10 @@ import helpers/prng_unsafe export - prng_unsafe, times, unittest, + unittest, # Generic sandwich ec_shortweierstrass_affine, ec_shortweierstrass_projective, - arithmetic, extension_fields, - io_extfields, - cyclotomic_subgroups, - gt_exponentiations, gt_exponentiations_vartime, - abstractions, algebras + extension_fields, + algebras type RandomGen* = enum @@ -65,7 +62,7 @@ func random_point*(rng: var RngState, EC: typedesc, randZ: bool, gen: RandomGen) result = rng.random_long01Seq(EC) result.clearCofactor() -template runPairingTests*(Name: static Algebra, G1, G2, GT: typedesc, Iters: int) = +proc runPairingTests*(Name: static Algebra, G1, G2, GT: typedesc, iters: int) = bind affineType var rng: RngState @@ -107,11 +104,11 @@ template runPairingTests*(Name: static Algebra, G1, G2, GT: typedesc, Iters: int suite "Pairing - Optimal Ate on " & $Name & " [" & $WordBitWidth & "-bit words]": test "Bilinearity e([2]P, Q) = e(P, [2]Q) = e(P, Q)^2": - test_bilinearity_double_impl(randZ = false, gen = Uniform, Iters) - test_bilinearity_double_impl(randZ = false, gen = HighHammingWeight, Iters) - test_bilinearity_double_impl(randZ = false, gen = Long01Sequence, Iters) + test_bilinearity_double_impl(randZ = false, gen = Uniform, iters) + test_bilinearity_double_impl(randZ = false, gen = HighHammingWeight, iters) + test_bilinearity_double_impl(randZ = false, gen = Long01Sequence, iters) -func random_elem(rng: var RngState, F: typedesc, gen: RandomGen): F {.inline, noInit.} = +func random_elem*(rng: var RngState, F: typedesc, gen: RandomGen): F {.noInit.} = if gen == Uniform: result = rng.random_unsafe(F) elif gen == HighHammingWeight: @@ -119,7 +116,7 @@ func random_elem(rng: var RngState, F: typedesc, gen: RandomGen): F {.inline, no else: result = rng.random_long01Seq(F) -template runGTsubgroupTests*(GT: typedesc, Iters: int) = +proc runGTsubgroupTests*(GT: typedesc, iters: int) = bind affineType, random_elem var rng: RngState @@ -147,21 +144,15 @@ template runGTsubgroupTests*(GT: typedesc, Iters: int) = suite "Pairing - π”Ύβ‚œ subgroup " & $GT.Name & " [" & $WordBitWidth & "-bit words]": test "Final Exponentiation and π”Ύβ‚œ-subgroup membership": - test_gt_impl(gen = Uniform, Iters) - test_gt_impl(gen = HighHammingWeight, Iters) - test_gt_impl(gen = Long01Sequence, Iters) - -func random_gt*(rng: var RngState, F: typedesc, gen: RandomGen): F {.inline, noInit.} = - if gen == Uniform: - result = rng.random_unsafe(F) - elif gen == HighHammingWeight: - result = rng.random_highHammingWeight(F) - else: - result = rng.random_long01Seq(F) + test_gt_impl(gen = Uniform, iters) + test_gt_impl(gen = HighHammingWeight, iters) + test_gt_impl(gen = Long01Sequence, iters) +func random_gt(rng: var RngState, F: typedesc, gen: RandomGen): F {.noInit.} = + result = rng.random_elem(F, gen) result.finalExp() -template runGTexponentiationTests*(GT: typedesc, Iters: int) = +proc runGTexponentiationTests*(GT: typedesc, iters: int) = var rng: RngState let timeseed = uint32(toUnix(getTime()) and (1'i64 shl 32 - 1)) # unixTime mod 2^32 seed(rng, timeseed) @@ -220,11 +211,11 @@ template runGTexponentiationTests*(GT: typedesc, Iters: int) = suite "Pairing - Exponentiation for π”Ύβ‚œ " & $GT.Name & " [" & $WordBitWidth & "-bit words]": test "π”Ύβ‚œ exponentiation consistency": - test_gt_exponentiation_impl(gen = Uniform, Iters) - test_gt_exponentiation_impl(gen = HighHammingWeight, Iters) - test_gt_exponentiation_impl(gen = Long01Sequence, Iters) + test_gt_exponentiation_impl(gen = Uniform, iters) + test_gt_exponentiation_impl(gen = HighHammingWeight, iters) + test_gt_exponentiation_impl(gen = Long01Sequence, iters) -proc runGTmultiexpTests*[N: static int](GT: typedesc, num_points: array[N, int], Iters: int) = +proc runGTmultiexpTests*[N: static int](GT: typedesc, num_points: array[N, int], iters: int) = var rng: RngState let timeseed = uint32(toUnix(getTime()) and (1'i64 shl 32 - 1)) # unixTime mod 2^32 seed(rng, timeseed) @@ -262,4 +253,4 @@ proc runGTmultiexpTests*[N: static int](GT: typedesc, num_points: array[N, int], suite "Pairing - MultiExponentiation for π”Ύβ‚œ " & $GT.Name & " [" & $WordBitWidth & "-bit words]": test "π”Ύβ‚œ multi-exponentiation consistency": - test_gt_multiexp_impl(GT, rng, num_points, gen = Long01Sequence, Iters) + test_gt_multiexp_impl(GT, rng, num_points, gen = Long01Sequence, iters) diff --git a/tests/parallel/t_pairing_bls12_381_gt_multiexp_parallel.nim b/tests/parallel/t_pairing_bls12_381_gt_multiexp_parallel.nim new file mode 100644 index 00000000..98fd820c --- /dev/null +++ b/tests/parallel/t_pairing_bls12_381_gt_multiexp_parallel.nim @@ -0,0 +1,18 @@ +# 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 + # Test utilities + ./t_pairing_template_parallel + +const numPoints = [1, 2, 8, 16, 128, 256, 1024] + +runGTmultiexp_parallel_Tests( + GT = Fp12[BLS12_381], + numPoints, + Iters = 4) diff --git a/tests/parallel/t_pairing_template_parallel.nim b/tests/parallel/t_pairing_template_parallel.nim new file mode 100644 index 00000000..cfa6db6a --- /dev/null +++ b/tests/parallel/t_pairing_template_parallel.nim @@ -0,0 +1,88 @@ +# 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 + # Standard library + std/unittest, times, + # Internals + constantine/platforms/abstractions, + constantine/math/extension_fields, + constantine/named/algebras, + constantine/math/pairings/[ + gt_exponentiations_vartime, + gt_multiexp_parallel, + pairings_generic], + constantine/threadpool, + + # Test utilities + helpers/prng_unsafe + +export + unittest, # generic sandwich + abstractions, # generic sandwich + extension_fields, + algebras + +type + RandomGen* = enum + Uniform + HighHammingWeight + Long01Sequence + +func random_elem(rng: var RngState, F: typedesc, gen: RandomGen): F {.noInit.} = + if gen == Uniform: + result = rng.random_unsafe(F) + elif gen == HighHammingWeight: + result = rng.random_highHammingWeight(F) + else: + result = rng.random_long01Seq(F) + +func random_gt(rng: var RngState, F: typedesc, gen: RandomGen): F {.noInit.} = + result = rng.random_elem(F, gen) + result.finalExp() + +proc runGTmultiexp_parallel_Tests*[N: static int](GT: typedesc, num_points: array[N, int], Iters: int) = + var rng: RngState + let timeseed = uint32(toUnix(getTime()) and (1'i64 shl 32 - 1)) # unixTime mod 2^32 + seed(rng, timeseed) + echo "\n------------------------------------------------------\n" + echo "test_pairing_",$GT.Name,"_gt_multiexp_parallel xoshiro512** seed: ", timeseed + + proc test_gt_multiexp_parallel_impl[N](GT: typedesc, rng: var RngState, num_points: array[N, int], gen: RandomGen, iters: int) = + let tp = Threadpool.new() + defer: tp.shutdown() + + for N in num_points: + stdout.write " " + for _ in 0 ..< iters: + var elems = newSeq[GT](N) + var exponents = newSeq[Fr[GT.Name]](N) + + for i in 0 ..< N: + elems[i] = rng.random_gt(GT, gen) + exponents[i] = rng.random_elem(Fr[GT.Name], gen) + + var naive: GT + naive.setOne() + for i in 0 ..< N: + var t {.noInit.}: GT + t.gtExp_vartime(elems[i], exponents[i]) + naive *= t + + var mexp: GT + tp.multiExp_vartime_parallel(mexp, elems, exponents) + + doAssert bool(naive == mexp) + + stdout.write '.' + + stdout.write '\n' + + suite "Pairing - Parallel MultiExponentiation for π”Ύβ‚œ " & $GT.Name & " [" & $WordBitWidth & "-bit words]": + test "Parallel π”Ύβ‚œ multi-exponentiation consistency": + test_gt_multiexp_parallel_impl(GT, rng, num_points, gen = Long01Sequence, Iters)