diff --git a/benchmarks/bench_fp12.nim b/benchmarks/bench_fp12.nim new file mode 100644 index 0000000..79622b6 --- /dev/null +++ b/benchmarks/bench_fp12.nim @@ -0,0 +1,54 @@ +# 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/tower_field_extensions/[abelian_groups, fp12_quad_fp6], + # Helpers + ../helpers/static_for, + ./bench_fields_template, + # Standard library + std/strutils + +# ############################################################ +# +# Benchmark of 𝔽p12 +# +# ############################################################ + + +const Iters = 10_000 +const InvIters = 1000 +const AvailableCurves = [ + # Pairing-Friendly curves + BN254, + BLS12_377, + BLS12_381, + BN446, + FKM12_447, + BLS12_461, + BN462 +] + +proc main() = + echo "-".repeat(80) + staticFor i, 0, AvailableCurves.len: + const curve = AvailableCurves[i] + addBench(Fp12[curve], Iters) + subBench(Fp12[curve], Iters) + negBench(Fp12[curve], Iters) + mulBench(Fp12[curve], Iters) + sqrBench(Fp12[curve], Iters) + invBench(Fp12[curve], InvIters) + echo "-".repeat(80) + +main() + +echo "Notes:" +echo " GCC is significantly slower than Clang on multiprecision arithmetic." diff --git a/benchmarks/bench_fp6.nim b/benchmarks/bench_fp6.nim index 2acede8..f62cb99 100644 --- a/benchmarks/bench_fp6.nim +++ b/benchmarks/bench_fp6.nim @@ -18,7 +18,7 @@ import # ############################################################ # -# Benchmark of 𝔽p2 = 𝔽p[𝑖] +# Benchmark of 𝔽p6 # # ############################################################ diff --git a/constantine.nimble b/constantine.nimble index 981052b..c947216 100644 --- a/constantine.nimble +++ b/constantine.nimble @@ -51,6 +51,7 @@ task test, "Run all tests": # Towers of extension fields test "", "tests/test_fp2.nim" test "", "tests/test_fp6.nim" + test "", "tests/test_fp12.nim" if sizeof(int) == 8: # 32-bit tests # Primitives @@ -74,6 +75,7 @@ task test, "Run all tests": # Towers of extension fields test "-d:Constantine32", "tests/test_fp2.nim" test "-d:Constantine32", "tests/test_fp6.nim" + test "-d:Constantine32", "tests/test_fp12.nim" task test_no_gmp, "Run tests that don't require GMP": # -d:testingCurves is configured in a *.nim.cfg for convenience @@ -95,6 +97,7 @@ task test_no_gmp, "Run tests that don't require GMP": # Towers of extension fields test "", "tests/test_fp2.nim" test "", "tests/test_fp6.nim" + test "", "tests/test_fp12.nim" if sizeof(int) == 8: # 32-bit tests # Primitives @@ -114,6 +117,7 @@ task test_no_gmp, "Run tests that don't require GMP": # Towers of extension fields test "-d:Constantine32", "tests/test_fp2.nim" test "-d:Constantine32", "tests/test_fp6.nim" + test "-d:Constantine32", "tests/test_fp12.nim" proc runBench(benchName: string, compiler = "") = if not dirExists "build": @@ -152,3 +156,12 @@ task bench_fp6_gcc, "Run benchmark 𝔽p6 with gcc": task bench_fp6_clang, "Run benchmark 𝔽p6 with clang": runBench("bench_fp6", "clang") + +task bench_fp12, "Run benchmark with 𝔽p12 your default compiler": + runBench("bench_fp12") + +task bench_fp12_gcc, "Run benchmark 𝔽p12 with gcc": + runBench("bench_fp12", "gcc") + +task bench_fp12_clang, "Run benchmark 𝔽p12 with clang": + runBench("bench_fp12", "clang") diff --git a/constantine/tower_field_extensions/fp12_quad_fp6.nim b/constantine/tower_field_extensions/fp12_quad_fp6.nim index 54958c5..07123a5 100644 --- a/constantine/tower_field_extensions/fp12_quad_fp6.nim +++ b/constantine/tower_field_extensions/fp12_quad_fp6.nim @@ -127,3 +127,27 @@ func prod*[C](r: var Fp12[C], a, b: Fp12[C]) = # r0 <- a0 b0 + γ a1 b1 r.c0 += Gamma * t + +func inv*[C](r: var Fp12[C], a: Fp12[C]) = + ## Compute the multiplicative inverse of ``a`` + # + # Algorithm: (the inverse exist if a != 0 which might cause constant-time issue) + # + # 1 / (a0 + a1 w) <=> (a0 - a1 w) / (a0 + a1 w)(a0 - a1 w) + # <=> (a0 - a1 w) / (a0² - a1² w²) + # In our case 𝔽p12 = 𝔽p6[γ], we have w² = γ + # So the inverse is (a0 - a1 w) / (a0² - γ a1²) + + # [2 Sqr, 1 Add] + var v0 {.noInit.}, v1 {.noInit.}: Fp6[C] + v0.square(a.c0) + v1.square(a.c1) + v0 -= Gamma * v1 # v0 = a0² - γ a1² (the norm / squared magnitude of a) + + # [1 Inv, 2 Sqr, 1 Add] + v1.inv(v0) # v1 = 1 / (a0² - γ a1²) + + # [1 Inv, 2 Mul, 2 Sqr, 1 Add, 1 Neg] + r.c0.prod(a.c0, v1) # r0 = a0 / (a0² - γ a1²) + v0.neg(v1) # v0 = -1 / (a0² - γ a1²) + r.c1.prod(a.c1, v0) # r1 = -a1 / (a0² - γ a1²) diff --git a/tests/test_fp12.nim b/tests/test_fp12.nim index e408eb1..584cf81 100644 --- a/tests/test_fp12.nim +++ b/tests/test_fp12.nim @@ -418,3 +418,33 @@ suite "𝔽p12 = 𝔽p6[√∛(1+𝑖)]": commutativeRing(FKM12_447) commutativeRing(BLS12_461) commutativeRing(BN462) + + test "𝔽p6 = 𝔽p2[∛(1+𝑖)] extension field multiplicative inverse": + proc mulInvOne(curve: static Curve) = + var one: Fp12[curve] + one.setOne() + + block: # Inverse of 1 is 1 + var r {.noInit.}: Fp12[curve] + r.inv(one) + check: bool(r == one) + + var aInv, r{.noInit.}: Fp12[curve] + + for _ in 0 ..< Iters: + let a = rng.random(Fp12[curve]) + + aInv.inv(a) + r.prod(a, aInv) + check: bool(r == one) + + r.prod(aInv, a) + check: bool(r == one) + + mulInvOne(BN254) + mulInvOne(BLS12_377) + mulInvOne(BLS12_381) + mulInvOne(BN446) + mulInvOne(FKM12_447) + mulInvOne(BLS12_461) + mulInvOne(BN462) diff --git a/tests/test_fp6.nim b/tests/test_fp6.nim index 8a582ab..d146160 100644 --- a/tests/test_fp6.nim +++ b/tests/test_fp6.nim @@ -431,7 +431,7 @@ suite "𝔽p6 = 𝔽p2[∛(1+𝑖)] (irreducible polynomial x³ - (1+𝑖))": var aInv, r{.noInit.}: Fp6[curve] - for _ in 0 ..< 1: # Iters: + for _ in 0 ..< Iters: let a = rng.random(Fp6[curve]) aInv.inv(a)