From e9ea05eb6b05ba0c6f49fbf1bcdec4ada6ed0e6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michele=20Orr=C3=B9?= Date: Wed, 28 Jun 2023 00:36:10 +0200 Subject: [PATCH 1/2] Add benchmarks. --- bench.js | 106 ++++++++++++++++++++++++++++++++++++++++++++++ package-lock.json | 15 ++++++- package.json | 6 ++- 3 files changed, 124 insertions(+), 3 deletions(-) create mode 100755 bench.js diff --git a/bench.js b/bench.js new file mode 100755 index 0000000..0c0deed --- /dev/null +++ b/bench.js @@ -0,0 +1,106 @@ +#!/usr/bin/env node + +// Measuring elliptic curve operations for ffjavascript +import { BigBuffer, buildBn128, buildBls12381, F1Field } from "ffjavascript"; +import { Bench } from "tinybench"; + +const curves = [ + buildBls12381(false).then((curve) => { + curve.name = "bls12_381"; + return curve; + }), + buildBn128(false), +]; + +function bench_add_ff(F) { + const x = F.random(); + const y = F.random(); + return () => { + F.add(x, y); + }; +} + +function bench_invert(F) { + const x = F.random(); + return () => { + F.invert(x); + }; +} + +function bench_add_ec(G, Fr) { + const x = G.timesScalar(G.g, Fr.random()); + const y = G.timesScalar(G.g, Fr.random()); + return () => { + G.add(x, y); + }; +} + +function bench_mul_ec(G, Fr) { + const x = G.F.random(); + const y = G.timesScalar(G.g, Fr.random()); + return () => { + G.timesScalar(y, x); + }; +} + +function bench_pairing(curve) { + const x = curve.Fr.random(); + const y = curve.Fr.random(); + const g1 = curve.G1.timesScalar(curve.G1.g, x); + const g2 = curve.G2.timesScalar(curve.G2.g, y); + return () => { + const pre1 = curve.prepareG1(g1); + const pre2 = curve.prepareG2(g2); + const r1 = curve.millerLoop(pre1, pre2); + const r2 = curve.finalExponentiation(r1); + }; +} + +function bench_group(bench, name, fn, range) { + for (var i = range[0]; i < range[1]; i++) { + bench.add(name + "/" + i, fn(i)); + } +} + +function bench_msm(G, Fr) { + return async (_n) => { + // size is log-scale + let n = Math.pow(2, _n); + const scalars = new BigBuffer(n * Fr.n8); + const bases = new BigBuffer(n * G.F.n8 * 2); + for (let i = 0; i < n; i++) { + const num = Fr.e(i + 1); + scalars.set(Fr.fromMontgomery(num), i * Fr.n8); + bases.set(G.toAffine(G.timesFr(G.g, num)), i * G.F.n8 * 2); + } + await G.multiExpAffine(bases, scalars, false, ""); + }; +} + +async function run() { + const bench = new Bench(); + + for (const curve of curves) { + const c = await curve; + + bench + .add(c.name + "/add_ff", bench_add_ff(c.Fr)) + .add(c.name + "/invert", bench_invert(c.Fr)) + .add(c.name + "/mul_G1", bench_mul_ec(c.G1, c.Fr)) + .add(c.name + "/mul_G2", bench_mul_ec(c.G2, c.Fr)) + .add(c.name + "/pairing", bench_pairing(c)) + .add(c.name + "/add_G1", bench_add_ec(c.G1, c.Fr)) + .add(c.name + "/add_G2", bench_add_ec(c.G2, c.Fr)); + + bench_group(bench, c.name + "/msm_G1", bench_msm(c.G1, c.Fr), [10, 16]); + bench_group(bench, c.name + "/msm_G2", bench_msm(c.G2, c.Fr), [10, 16]); + } + + await bench.run(); + process.stdout.write(JSON.stringify(bench.table())); + return bench.results; +} + +run().then(() => { + process.exit(0); +}); diff --git a/package-lock.json b/package-lock.json index a7587b4..cd41be7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,8 @@ "chai": "^4.2.0", "eslint": "^8.0.1", "mocha": "^10.0.0", - "rollup": "^2.38.5" + "rollup": "^2.38.5", + "tinybench": "^2.5.0" } }, "node_modules/@eslint-community/eslint-utils": { @@ -1621,6 +1622,12 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "node_modules/tinybench": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.5.0.tgz", + "integrity": "sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==", + "dev": true + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3007,6 +3014,12 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "tinybench": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.5.0.tgz", + "integrity": "sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==", + "dev": true + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", diff --git a/package.json b/package.json index 5e9963a..ebd50da 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ }, "scripts": { "test": "mocha", - "build": "rollup -c rollup.cjs.config.js" + "build": "rollup -c rollup.cjs.config.js", + "bench": "node ./bench.js" }, "repository": { "type": "git", @@ -41,6 +42,7 @@ "chai": "^4.2.0", "eslint": "^8.0.1", "mocha": "^10.0.0", - "rollup": "^2.38.5" + "rollup": "^2.38.5", + "tinybench": "^2.5.0" } } From e74ad8deeef6d5d114165b0e8d53a3e9d922bac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michele=20Orr=C3=B9?= Date: Fri, 3 Nov 2023 19:58:09 +0100 Subject: [PATCH 2/2] Add more benchmarks. --- bench.js | 90 +++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 56 insertions(+), 34 deletions(-) diff --git a/bench.js b/bench.js index 0c0deed..e4723bc 100755 --- a/bench.js +++ b/bench.js @@ -4,13 +4,7 @@ import { BigBuffer, buildBn128, buildBls12381, F1Field } from "ffjavascript"; import { Bench } from "tinybench"; -const curves = [ - buildBls12381(false).then((curve) => { - curve.name = "bls12_381"; - return curve; - }), - buildBn128(false), -]; +const curves = [buildBn128(false), buildBls12381(false)]; function bench_add_ff(F) { const x = F.random(); @@ -20,6 +14,14 @@ function bench_add_ff(F) { }; } +function bench_mul_ff(F) { + const x = F.random(); + const y = F.random(); + return () => { + F.mul(x, y); + }; +} + function bench_invert(F) { const x = F.random(); return () => { @@ -49,32 +51,36 @@ function bench_pairing(curve) { const g1 = curve.G1.timesScalar(curve.G1.g, x); const g2 = curve.G2.timesScalar(curve.G2.g, y); return () => { - const pre1 = curve.prepareG1(g1); - const pre2 = curve.prepareG2(g2); - const r1 = curve.millerLoop(pre1, pre2); - const r2 = curve.finalExponentiation(r1); + curve.pairing(g1, g2); }; } -function bench_group(bench, name, fn, range) { +function bench_fft(bench, name, F, range) { for (var i = range[0]; i < range[1]; i++) { - bench.add(name + "/" + i, fn(i)); + const n = Math.pow(2, i); + const x = new BigBuffer(n * F.n8); + for (let i = 0; i < n; i++) { + x.set(F.random(), i * F.n8); + } + bench.add(name + "/" + n, async () => { + await F.fft(x); + }); } } -function bench_msm(G, Fr) { - return async (_n) => { - // size is log-scale - let n = Math.pow(2, _n); +function bench_msm(bench, name, G, Fr, range) { + for (var i = range[0]; i < range[1]; i++) { + let n = Math.pow(2, i); const scalars = new BigBuffer(n * Fr.n8); const bases = new BigBuffer(n * G.F.n8 * 2); for (let i = 0; i < n; i++) { - const num = Fr.e(i + 1); - scalars.set(Fr.fromMontgomery(num), i * Fr.n8); - bases.set(G.toAffine(G.timesFr(G.g, num)), i * G.F.n8 * 2); + scalars.set(Fr.random(), i * Fr.n8); + bases.set(G.toAffine(G.timesFr(G.g, Fr.random())), i * G.F.n8 * 2); } - await G.multiExpAffine(bases, scalars, false, ""); - }; + bench.add(name + "/" + n, async () => { + await G.multiExpAffine(bases, scalars, false, ""); + }); + } } async function run() { @@ -82,23 +88,39 @@ async function run() { for (const curve of curves) { const c = await curve; + // If we do not do it like that, then the pairing operation does not work + // properly. + let name = c.name; + if (name == "bls12381") { + name = "bls12_381"; + } + if (name == "bn128") { + name = "bn254"; + } bench - .add(c.name + "/add_ff", bench_add_ff(c.Fr)) - .add(c.name + "/invert", bench_invert(c.Fr)) - .add(c.name + "/mul_G1", bench_mul_ec(c.G1, c.Fr)) - .add(c.name + "/mul_G2", bench_mul_ec(c.G2, c.Fr)) - .add(c.name + "/pairing", bench_pairing(c)) - .add(c.name + "/add_G1", bench_add_ec(c.G1, c.Fr)) - .add(c.name + "/add_G2", bench_add_ec(c.G2, c.Fr)); - - bench_group(bench, c.name + "/msm_G1", bench_msm(c.G1, c.Fr), [10, 16]); - bench_group(bench, c.name + "/msm_G2", bench_msm(c.G2, c.Fr), [10, 16]); + .add(name + "/add_ff", bench_add_ff(c.Fr)) + .add(name + "/mul_ff", bench_mul_ff(c.Fr)) + .add(name + "/invert", bench_invert(c.Fr)) + .add(name + "/mul_G1", bench_mul_ec(c.G1, c.Fr)) + .add(name + "/mul_G2", bench_mul_ec(c.G2, c.Fr)) + .add(name + "/pairing", bench_pairing(c)) + .add(name + "/add_G1", bench_add_ec(c.G1, c.Fr)) + .add(name + "/add_G2", bench_add_ec(c.G2, c.Fr)); + + bench_msm(bench, name + "/msm_G1", c.G1, c.Fr, [1, 21]); + bench_msm(bench, name + "/msm_G2", c.G2, c.Fr, [1, 21]); + bench_fft(bench, name + "/fft", c.Fr, [1, 21]); } await bench.run(); - process.stdout.write(JSON.stringify(bench.table())); - return bench.results; + console.table(bench.table()); + const results = bench.tasks.map((x) => { + // remove samples to avoid the dataset from exploding + let { ["samples"]: unused, ...info } = x.result; + return { name: x.name, ...info }; + }); + process.stderr.write(JSON.stringify(results)); } run().then(() => {