Skip to content

Commit 070257f

Browse files
🚀 perf: Draft for benchmarking code.
1 parent 6e3835e commit 070257f

File tree

3 files changed

+406
-0
lines changed

3 files changed

+406
-0
lines changed

_benchmark/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/yarn.lock

_benchmark/index.js

Lines changed: 381 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,381 @@
1+
/* global BigInt */
2+
/* eslint-disable new-cap, no-new, no-unused-expressions */
3+
4+
const { ZZ } = require('..') ;
5+
var benchmark = require('benchmark');
6+
var crypto = require('crypto');
7+
var bn = require('bn.js');
8+
var bignum;
9+
try {
10+
bignum = require('bignum');
11+
} catch (err) {
12+
console.log('Load bignum error: ' + err.message.split('\n')[0]);
13+
}
14+
var sjcl = require('eccjs').sjcl.bn;
15+
var bigi = require('bigi');
16+
var BigInteger = require('js-big-integer').BigInteger;
17+
var JSBI = require('jsbi');
18+
var SilentMattBigInteger = require('biginteger').BigInteger;
19+
var XorShift128Plus = require('xorshift.js').XorShift128Plus;
20+
var benchmarks = [];
21+
22+
var selfOnly = process.env.SELF_ONLY;
23+
var seed = process.env.SEED || crypto.randomBytes(16).toString('hex');
24+
console.log('Seed: ' + seed);
25+
var prng = new XorShift128Plus(seed);
26+
27+
const NFIXTURES = 25;
28+
var fixtures = [];
29+
var findex = 0;
30+
function findexRefresh () {
31+
if (++findex === fixtures.length) findex = 0;
32+
}
33+
34+
const filter = process.argv[3] ? new RegExp(process.argv[3], 'i') : /./;
35+
36+
const k256 = 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f' ;
37+
38+
function add (op, ...args) {
39+
benchmarks.push({
40+
name: op,
41+
start: function start () {
42+
var suite = new benchmark.Suite();
43+
44+
console.log('Benchmarking: ' + op);
45+
46+
Object.keys(fns).forEach(function (name) {
47+
if (!filter.test(name)) return ;
48+
if (name === 'BigInt' && typeof BigInt === 'undefined') return ;
49+
if (name === 'bignum' && bignum === undefined) return ;
50+
51+
if (!selfOnly || name === '@aureooms/js-integer') {
52+
const opFn = fns[name][op];
53+
if (!opFn) return;
54+
if (!(opFn instanceof Function)) throw new Error(`opFN is not a function: ${opFN}`) ;
55+
const fixture = fixtures[findex][name];
56+
57+
if (args.length === 1) {
58+
const key = op + '-' + args[0];
59+
const x = fixture.args[args[0]];
60+
const outs = fixture.outs;
61+
const testFn = () => outs[key] = opFn(x) ;
62+
suite.add(name + '#' + key, testFn, {
63+
onStart: findexRefresh,
64+
onCycle: findexRefresh
65+
});
66+
}
67+
else if (args.length === 2) {
68+
const key = op + '-' + args[0] + '-' + args[1];
69+
const a = fixture.args[args[0]];
70+
const b = fixture.args[args[1]];
71+
const outs = fixture.outs;
72+
const testFn = () => outs[key] = opFn(a, b) ;
73+
suite.add(name + '#' + key, testFn, {
74+
onStart: findexRefresh,
75+
onCycle: findexRefresh
76+
});
77+
}
78+
else throw new Error('Too many args.') ;
79+
}
80+
});
81+
82+
suite
83+
.on('cycle', function (event) {
84+
console.log(String(event.target));
85+
})
86+
.on('complete', function () {
87+
console.log('------------------------');
88+
console.log('Fastest is ' + this.filter('fastest')[0].name);
89+
})
90+
.run();
91+
92+
console.log('========================');
93+
}
94+
});
95+
}
96+
97+
function start () {
98+
var re = process.argv[2] ? new RegExp(process.argv[2], 'i') : /./;
99+
100+
benchmarks
101+
.filter(function (b) {
102+
return re.test(b.name);
103+
})
104+
.forEach(function (b) {
105+
b.start();
106+
});
107+
}
108+
109+
if (/fast/i.test(process.argv[4])) {
110+
console.log('Running in fast mode...');
111+
benchmark.options.minTime = 0.3;
112+
benchmark.options.maxTime = 1;
113+
benchmark.options.minSamples = 3;
114+
} else {
115+
benchmark.options.minTime = 1;
116+
}
117+
118+
const fns = {
119+
BigInt: {
120+
from10: s => BigInt(s),
121+
from16: s => BigInt('0x'+s),
122+
to10: x => x.toString(10),
123+
to16: x => x.toString(16),
124+
add: (a,b) => a + b,
125+
sub: (a,b) => a - b,
126+
mul: (a,b) => a * b,
127+
sqr: x => x**2n,
128+
pow: (a,b) => a**b,
129+
div: (a,b) => a / b,
130+
mod: (a, b) => {
131+
const remainder = a % b;
132+
return remainder < 0 ? remainder + b : remainer;
133+
},
134+
135+
toRed: x => x % k256_BigInt,
136+
fromRed: x => x,
137+
sqrm: x => (a**2) % k256_BigInt,
138+
powm: (a,b) => (a**b) % k256_BigInt,
139+
} ,
140+
'bn.js': {
141+
from10: s => new bn(s, 10),
142+
from16: s => new bn(s, 16),
143+
to10: x => x.toString(10),
144+
to16: x => x.toString(16),
145+
add: (a,b) => a.add(b),
146+
sub: (a,b) => a.sub(b),
147+
mul: (a,b) => a.mul(b),
148+
sqr: x => x.mul(x),
149+
pow: (a,b) => a.pow(b),
150+
div: (a,b) => a.div(b),
151+
mod: (a,b) => a.mod(b),
152+
153+
toRed: x => x.toRed(bn.red('k256')),
154+
fromRed: x => x.fromRed(),
155+
sqrm: x => x.redSqr(),
156+
powm: (a,b) => a.redPow(b),
157+
invm: x => x.redInvm(),
158+
159+
gcd: (a,b) => a.gcd(b),
160+
egcd: (a,b) => a.egcd(b),
161+
} ,
162+
'@aureooms/js-integer': {
163+
from10: s => ZZ.from(s, 10),
164+
from16: s => ZZ.from(s, 16),
165+
to10: x => x.toString(10),
166+
to16: x => x.toString(16),
167+
add: (a,b) => a.add(b),
168+
sub: (a,b) => a.sub(b),
169+
mul: (a,b) => a.mul(b),
170+
sqr: x => x.square(),
171+
pow: (a,b) => a.pow(b),
172+
div: (a,b) => a.div(b),
173+
mod: (a,b) => a.mod(b),
174+
175+
gcd: (a,b) => a.gcd(b),
176+
egcd: (a,b) => a.egcd(b),
177+
} ,
178+
179+
jsbi: {
180+
from10: s => JSBI.BigInt(s),
181+
from16: s => JSBI.BigInt('0x'+s),
182+
to10: x => x.toString(10),
183+
to16: x => x.toString(16),
184+
add: (a,b) => JSBI.add(a, b),
185+
sub: (a,b) => JSBI.subtract(a, b),
186+
mul: (a,b) => JSBI.multiply(a, b),
187+
sqr: x => JSBI.multiply(x, x),
188+
pow: (a, b) => JSBI.exponentiate(a, b),
189+
div: (a,b) => JSBI.divide(a, b),
190+
mod: (a, b) => {
191+
const remainder = JSBI.remainder(a,b);
192+
return JSBI.lessThan(remainder,JSBI.BigInt(0))
193+
? JSBI.add(remainder,b)
194+
: remainder;
195+
},
196+
},
197+
yaffle: {
198+
from10: s => BigInteger.BigInt(s),
199+
from16: s => BigInteger.BigInt('0x'+s),
200+
to10: x => x.toString(10),
201+
to16: x => x.toString(16),
202+
add: (a,b) => BigInteger.add(a, b),
203+
sub: (a,b) => BigInteger.subtract(a, b),
204+
mul: (a,b) => BigInteger.multiply(a, b),
205+
sqr: x => BigInteger.multiply(x, x),
206+
pow: (a, b) => BigInteger.exponentiate(a, b),
207+
div: (a,b) => BigInteger.divide(a, b),
208+
mod: (a, b) => {
209+
const remainder = BigInteger.remainder(a,b);
210+
return BigInteger.lessThan(remainder,BigInteger.BigInt(0))
211+
? BigInteger.add(remainder,b)
212+
: remainder;
213+
},
214+
},
215+
bigi: {
216+
from10: s => new bigi(s, 10),
217+
from16: s => new bigi(s, 16),
218+
to10: x => x.toString(10),
219+
to16: x => x.toString(16),
220+
add: (a,b) => a.add(b),
221+
sub: (a,b) => a.subtract(b),
222+
mul: (a,b) => a.multiply(b),
223+
sqr: x => x.square(),
224+
pow: (a,b) => a.pow(b),
225+
div: (a,b) => a.divide(b),
226+
mod: (a,b) => a.remainder(b),
227+
228+
gcd: (a,b) => a.gcd(b),
229+
},
230+
'silentmatt-biginteger': {
231+
from10: s => SilentMattBigInteger.parse(s, 10),
232+
from16: s => SilentMattBigInteger.parse(s, 16),
233+
to10: x => x.toString(10),
234+
to16: x => x.toString(16),
235+
add: (a,b) => a.add(b),
236+
sub: (a,b) => a.subtract(b),
237+
mul: (a,b) => a.multiply(b),
238+
sqr: x => x.square(x),
239+
pow: (a,b) => a.pow(b),
240+
div: (a,b) => a.quotient(b),
241+
mod: (a, b) => {
242+
const remainder = a.remainder(b);
243+
return remainder.isNegative()
244+
? remainder.add(b)
245+
: remainder;
246+
},
247+
},
248+
bignum: {
249+
from10: s => new bignum(s, 10),
250+
from16: s => new bignum(s, 16),
251+
to10: x => x.toString(10),
252+
to16: x => x.toString(16).replace(/^0+/, ''),
253+
add: (a,b) => a.add(b),
254+
sub: (a,b) => a.sub(b),
255+
mul: (a,b) => a.mul(b),
256+
sqr: x => x.mul(x),
257+
pow: (a,b) => a.pow(b),
258+
div: (a,b) => a.div(b),
259+
mod: (a,b) => a.mod(b),
260+
fromRed: x => x,
261+
toRed: x => x.mod(k256_bignum),
262+
powm: (a,b) => a.powm(b, k256_bignum),
263+
sqrm: x => a.pown(2, k256_bignum),
264+
invm: x => x.invertm(k256_bignum),
265+
} ,
266+
sjcl: {
267+
from10: undefined,
268+
from16: s => new sjcl(s),
269+
to10: undefined,
270+
to16: x => x.toString().slice(2),
271+
add: (a,b) => a.add(b),
272+
sub: (a,b) => a.sub(b),
273+
mul: (a,b) => a.mul(b),
274+
sqr: x => x.mul(x),
275+
pow: (a,b) => a.power(b),
276+
fromRed: x => x,
277+
toRed: x => new sjcl.prime.p256k(x),
278+
invm: x => x.inverseMod(k256_sjcl),
279+
sqrm: x => x.square().fullReduce(),
280+
}
281+
282+
} ;
283+
284+
const k256_BigInt = fns.BigInt.from16(k256);
285+
const k256_bignum = fns.bignum.from16(k256);
286+
const k256_sjcl = fns.sjcl.from16(k256);
287+
288+
fns.BigInt.k256 = k256_BigInt ;
289+
fns.bignum.k256 = k256_bignum ;
290+
fns.sjcl.k256 = k256_sjcl ;
291+
fns['bn.js'].k256 = 'k256' ;
292+
293+
function newFixture ( ) {
294+
const fixture = {};
295+
296+
const a = prng.randomBytes(32).toString('hex');
297+
const b = prng.randomBytes(32).toString('hex');
298+
const aj = prng.randomBytes(768).toString('hex');
299+
const bj = prng.randomBytes(768).toString('hex');
300+
301+
const a10base = new bn(a, 16).toString(10);
302+
const a16base = new bn(a, 16).toString(16);
303+
304+
const init = fn => {
305+
const a1 = fn.from16(a);
306+
const b1 = fn.from16(b);
307+
const a1j = fn.from16(aj);
308+
const b1j = fn.from16(bj);
309+
const x = fn.from16('2adbeef');
310+
const as1 = fn.add(fn.sqr(a1), x);
311+
const am1 = fn.toRed && fn.toRed(a1);
312+
const pow1 = fn.fromRed && fn.fromRed(am1);
313+
return {
314+
a1, b1, a1j, b1j, as1, am1, pow1, a10base, a16base,
315+
} ;
316+
}
317+
318+
Object.keys(fns).forEach( name => {
319+
fixture[name] = {};
320+
fixture[name].args = init(fns[name]);
321+
fixture[name].outs = {};
322+
}) ;
323+
324+
return fixture;
325+
326+
}
327+
328+
while (fixtures.length < NFIXTURES) fixtures.push(newFixture()) ;
329+
330+
add('from10', 'a10base');
331+
add('from16', 'a16base');
332+
add('to10', 'a1');
333+
add('to16', 'a1');
334+
add('add', 'a1', 'b1');
335+
add('add', 'a1j', 'b1j');
336+
add('sub', 'a1', 'b1');
337+
add('sub', 'a1j', 'b1j');
338+
add('mul', 'a1', 'b1');
339+
add('mul', 'a1j', 'b1j');
340+
add('sqr', 'a1');
341+
add('div', 'as1', 'a1');
342+
add('mod', 'as1', 'a1');
343+
add('sqrm', 'am1');
344+
add('powm', 'am1', 'pow1');
345+
add('invm', 'am1');
346+
add('gcd', 'a1', 'b1');
347+
add('egcd', 'a1', 'b1');
348+
349+
start();
350+
351+
352+
// Forces ALL computations ?
353+
const results = new Array(NFIXTURES) ;
354+
for ( let i = 0; i < NFIXTURES; ++i ) {
355+
results[i] = {};
356+
Object.keys(fns).forEach( name => {
357+
const fn = fns[name];
358+
const fixture = fixtures[i][name];
359+
Object.keys(fixture.outs).forEach( key => {
360+
results[i][key] = results[i][key] || {} ;
361+
362+
const result = fixture.outs[key] ;
363+
const str = result instanceof String ? result : fn.to16(result).toLowerCase() ;
364+
results[i][key][name] = str;
365+
}) ;
366+
} ) ;
367+
368+
for ( const key in results[i] ) {
369+
const theseResults = results[i][key] ;
370+
const distinctResults = new Set(Object.values(theseResults));
371+
if (distinctResults.size !== 1) {
372+
console.error('DIFFERENT OUTPUTS for', i, key) ;
373+
console.error(distinctResults) ;
374+
console.error(i, key, theseResults) ;
375+
}
376+
else {
377+
console.error(i, key, JSON.stringify(Object.keys(theseResults)), 'OK') ;
378+
}
379+
}
380+
381+
}

0 commit comments

Comments
 (0)