367 lines
11 KiB
JavaScript
367 lines
11 KiB
JavaScript
|
|
||
|
var arg, i, max, method, methodIndex, decimalPlaces,
|
||
|
reps, rounding, start, timesEqual, Xs, Ys,
|
||
|
bdM, bdMT, bdOT, bdRs, bdXs, bdYs,
|
||
|
bnM, bnMT, bnOT, bnRs, bnXs, bnYs,
|
||
|
memoryUsage, showMemory, bnR, bdR,
|
||
|
prevRss, prevHeapUsed, prevHeapTotal,
|
||
|
args = process.argv.splice(2),
|
||
|
BigDecimal = require('./lib/bigdecimal_GWT/bigdecimal').BigDecimal,
|
||
|
BigNumber = require('../bignumber'),
|
||
|
bdMs = ['add', 'subtract', 'multiply', 'divide', 'remainder', 'compareTo', 'pow'],
|
||
|
bnMs1 = ['plus', 'minus', 'times', 'dividedBy', 'modulo', 'comparedTo', 'toPower'],
|
||
|
bnMs2 = ['', '', '', 'div', 'mod', 'cmp', ''],
|
||
|
Ms = [bdMs, bnMs1, bnMs2],
|
||
|
allMs = [].concat.apply([], Ms),
|
||
|
expTotal = 0,
|
||
|
total = 0,
|
||
|
|
||
|
ALWAYS_SHOW_MEMORY = false,
|
||
|
DEFAULT_MAX_DIGITS = 20,
|
||
|
DEFAULT_POW_MAX_DIGITS = 20,
|
||
|
DEFAULT_REPS = 1e4,
|
||
|
DEFAULT_POW_REPS = 1e2,
|
||
|
DEFAULT_PLACES = 20,
|
||
|
MAX_POWER = 50,
|
||
|
|
||
|
getRandom = function (maxDigits) {
|
||
|
var i = 0, z,
|
||
|
// number of digits - 1
|
||
|
n = Math.random() * ( maxDigits || 1 ) | 0,
|
||
|
r = ( Math.random() * 10 | 0 ) + '';
|
||
|
|
||
|
if ( n ) {
|
||
|
if ( z = r === '0' ) {
|
||
|
r += '.';
|
||
|
}
|
||
|
|
||
|
for ( ; i++ < n; r += Math.random() * 10 | 0 ){}
|
||
|
|
||
|
// 20% chance of integer
|
||
|
if ( !z && Math.random() > 0.2 )
|
||
|
r = r.slice( 0, i = ( Math.random() * n | 0 ) + 1 ) + '.' + r.slice(i);
|
||
|
}
|
||
|
|
||
|
// Avoid 'division by zero' error with division and modulo.
|
||
|
if ((bdM == 'divide' || bdM == 'remainder') && parseFloat(r) === 0)
|
||
|
r = ( ( Math.random() * 9 | 0 ) + 1 ) + '';
|
||
|
|
||
|
total += n + 1;
|
||
|
|
||
|
// 50% chance of negative
|
||
|
return Math.random() > 0.5 ? r : '-' + r;
|
||
|
},
|
||
|
|
||
|
pad = function (str) {
|
||
|
str += '... ';
|
||
|
while (str.length < 26) str += ' ';
|
||
|
return str;
|
||
|
},
|
||
|
|
||
|
getFastest = function (bn, bd) {
|
||
|
var r;
|
||
|
if (Math.abs(bn - bd) > 2) {
|
||
|
r = 'Big' + ((bn < bd)
|
||
|
? 'Number ' + (bn ? parseFloat((bd / bn).toFixed(1)) : bd)
|
||
|
: 'Decimal ' + (bd ? parseFloat((bn / bd).toFixed(1)) : bn)) +
|
||
|
' times faster';
|
||
|
} else {
|
||
|
timesEqual = 1;
|
||
|
r = 'Times approximately equal';
|
||
|
}
|
||
|
return r;
|
||
|
},
|
||
|
|
||
|
showMemoryChange = function () {
|
||
|
if (showMemory) {
|
||
|
memoryUsage = process.memoryUsage();
|
||
|
|
||
|
var rss = memoryUsage.rss,
|
||
|
heapUsed = memoryUsage.heapUsed,
|
||
|
heapTotal = memoryUsage.heapTotal;
|
||
|
|
||
|
console.log(' Change in memory usage: ' +
|
||
|
' rss: ' + toKB(rss - prevRss) +
|
||
|
', hU: ' + toKB(heapUsed - prevHeapUsed) +
|
||
|
', hT: ' + toKB(heapTotal - prevHeapTotal));
|
||
|
prevRss = rss; prevHeapUsed = heapUsed; prevHeapTotal = heapTotal;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
toKB = function (m) {
|
||
|
return parseFloat((m / 1024).toFixed(1)) + ' KB';
|
||
|
};
|
||
|
|
||
|
|
||
|
// PARSE COMMAND LINE AND SHOW HELP
|
||
|
|
||
|
if (arg = args[0], typeof arg != 'undefined' && !isFinite(arg) &&
|
||
|
allMs.indexOf(arg) == -1 && !/^-*m$/i.test(arg)) {
|
||
|
console.log(
|
||
|
'\n node bigtime-OOM [METHOD] [METHOD CALLS [MAX DIGITS [DECIMAL PLACES]]]\n' +
|
||
|
'\n METHOD: The method to be timed and compared with the automatically' +
|
||
|
'\n chosen corresponding method from BigDecimal or BigNumber\n' +
|
||
|
'\n BigDecimal: add subtract multiply divide remainder compareTo pow' +
|
||
|
'\n BigNumber: plus minus times dividedBy modulo comparedTo toPower' +
|
||
|
'\n (div mod cmp pow)' +
|
||
|
'\n\n METHOD CALLS: The number of method calls to be timed' +
|
||
|
'\n\n MAX DIGITS: The maximum number of digits of the random ' +
|
||
|
'\n numbers used in the method calls' +
|
||
|
'\n\n DECIMAL PLACES: The number of decimal places used in division' +
|
||
|
'\n (The rounding mode is randomly chosen)' +
|
||
|
'\n\n Default values: METHOD: randomly chosen' +
|
||
|
'\n METHOD CALLS: ' + DEFAULT_REPS +
|
||
|
' (pow: ' + DEFAULT_POW_REPS + ')' +
|
||
|
'\n MAX DIGITS: ' + DEFAULT_MAX_DIGITS +
|
||
|
' (pow: ' + DEFAULT_POW_MAX_DIGITS + ')' +
|
||
|
'\n DECIMAL PLACES: ' + DEFAULT_PLACES + '\n' +
|
||
|
'\n E.g.s node bigtime-OOM\n node bigtime-OOM minus' +
|
||
|
'\n node bigtime-OOM add 100000' +
|
||
|
'\n node bigtime-OOM times 20000 100' +
|
||
|
'\n node bigtime-OOM div 100000 50 20' +
|
||
|
'\n node bigtime-OOM 9000' +
|
||
|
'\n node bigtime-OOM 1000000 20\n' +
|
||
|
'\n To show memory usage include an argument m or -m' +
|
||
|
'\n E.g. node bigtime-OOM m add');
|
||
|
} else {
|
||
|
BigNumber.config({EXPONENTIAL_AT : 1E9, RANGE : 1E9});
|
||
|
Number.prototype.toPlainString = Number.prototype.toString;
|
||
|
|
||
|
for (i = 0; i < args.length; i++) {
|
||
|
arg = args[i];
|
||
|
|
||
|
if (isFinite(arg)) {
|
||
|
arg = Math.abs(parseInt(arg));
|
||
|
if (reps == null) {
|
||
|
reps = arg <= 1e10 ? arg : 0;
|
||
|
} else if (max == null) {
|
||
|
max = arg <= 1e6 ? arg : 0;
|
||
|
} else if (decimalPlaces == null) {
|
||
|
decimalPlaces = arg <= 1e6 ? arg : DEFAULT_PLACES;
|
||
|
}
|
||
|
} else if (/^-*m$/i.test(arg)) {
|
||
|
showMemory = true;
|
||
|
} else if (method == null) {
|
||
|
method = arg;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (i = 0;
|
||
|
i < Ms.length && (methodIndex = Ms[i].indexOf(method)) == -1;
|
||
|
i++) {}
|
||
|
|
||
|
bnM = methodIndex == -1
|
||
|
? bnMs1[methodIndex = Math.floor(Math.random() * bdMs.length)]
|
||
|
: (Ms[i][0] == 'add' ? bnMs1 : Ms[i])[methodIndex];
|
||
|
|
||
|
bdM = bdMs[methodIndex];
|
||
|
|
||
|
if (!reps)
|
||
|
reps = bdM == 'pow' ? DEFAULT_POW_REPS : DEFAULT_REPS;
|
||
|
if (!max)
|
||
|
max = bdM == 'pow' ? DEFAULT_POW_MAX_DIGITS : DEFAULT_MAX_DIGITS;
|
||
|
if (decimalPlaces == null)
|
||
|
decimalPlaces = DEFAULT_PLACES;
|
||
|
|
||
|
Xs = [reps], Ys = [reps];
|
||
|
bdXs = [reps], bdYs = [reps], bdRs = [reps];
|
||
|
bnXs = [reps], bnYs = [reps], bnRs = [reps];
|
||
|
showMemory = showMemory || ALWAYS_SHOW_MEMORY;
|
||
|
|
||
|
console.log('\n BigNumber %s vs BigDecimal %s', bnM, bdM);
|
||
|
console.log('\n Method calls: %d', reps);
|
||
|
|
||
|
if (bdM == 'divide') {
|
||
|
rounding = Math.floor(Math.random() * 7);
|
||
|
console.log('\n Decimal places: %d\n Rounding mode: %d', decimalPlaces, rounding);
|
||
|
BigNumber.config(decimalPlaces, rounding);
|
||
|
}
|
||
|
|
||
|
if (showMemory) {
|
||
|
memoryUsage = process.memoryUsage();
|
||
|
console.log(' Memory usage: rss: ' +
|
||
|
toKB(prevRss = memoryUsage.rss) + ', hU: ' +
|
||
|
toKB(prevHeapUsed = memoryUsage.heapUsed) + ', hT: ' +
|
||
|
toKB(prevHeapTotal = memoryUsage.heapTotal));
|
||
|
}
|
||
|
|
||
|
|
||
|
// CREATE RANDOM NUMBERS
|
||
|
|
||
|
// POW: BigDecimal requires JS Number type for exponent argument
|
||
|
if (bdM == 'pow') {
|
||
|
|
||
|
process.stdout.write('\n Creating ' + reps +
|
||
|
' random numbers (max. digits: ' + max + ')... ');
|
||
|
|
||
|
for (i = 0; i < reps; i++) {
|
||
|
Xs[i] = getRandom(max);
|
||
|
}
|
||
|
console.log('done\n Average number of digits: %d',
|
||
|
((total / reps) | 0));
|
||
|
|
||
|
process.stdout.write(' Creating ' + reps +
|
||
|
' random integer exponents (max. value: ' + MAX_POWER + ')... ');
|
||
|
|
||
|
for (i = 0; i < reps; i++) {
|
||
|
bdYs[i] = bnYs[i] = Math.floor(Math.random() * (MAX_POWER + 1));
|
||
|
expTotal += bdYs[i];
|
||
|
}
|
||
|
console.log('done\n Average value: %d', ((expTotal / reps) | 0));
|
||
|
|
||
|
showMemoryChange();
|
||
|
|
||
|
|
||
|
// POW: TIME CREATION OF BIGDECIMALS
|
||
|
|
||
|
process.stdout.write('\n Creating BigDecimals... ');
|
||
|
|
||
|
start = +new Date();
|
||
|
for (i = 0; i < reps; i++) {
|
||
|
bdXs[i] = new BigDecimal(Xs[i]);
|
||
|
}
|
||
|
bdOT = +new Date() - start;
|
||
|
|
||
|
console.log('done. Time taken: %s ms', bdOT || '<1');
|
||
|
|
||
|
showMemoryChange();
|
||
|
|
||
|
|
||
|
// POW: TIME CREATION OF BIGNUMBERS
|
||
|
|
||
|
process.stdout.write(' Creating BigNumbers... ');
|
||
|
|
||
|
start = +new Date();
|
||
|
for (i = 0; i < reps; i++) {
|
||
|
bnXs[i] = new BigNumber(Xs[i]);
|
||
|
}
|
||
|
bnOT = +new Date() - start;
|
||
|
|
||
|
console.log('done. Time taken: %s ms', bnOT || '<1');
|
||
|
|
||
|
|
||
|
// NOT POW
|
||
|
} else {
|
||
|
|
||
|
process.stdout.write('\n Creating ' + (reps * 2) +
|
||
|
' random numbers (max. digits: ' + max + ')... ');
|
||
|
|
||
|
|
||
|
for (i = 0; i < reps; i++) {
|
||
|
Xs[i] = getRandom(max);
|
||
|
Ys[i] = getRandom(max);
|
||
|
}
|
||
|
console.log('done\n Average number of digits: %d',
|
||
|
( total / (reps * 2) ) | 0);
|
||
|
|
||
|
showMemoryChange();
|
||
|
|
||
|
|
||
|
// TIME CREATION OF BIGDECIMALS
|
||
|
|
||
|
process.stdout.write('\n Creating BigDecimals... ');
|
||
|
|
||
|
start = +new Date();
|
||
|
for (i = 0; i < reps; i++) {
|
||
|
bdXs[i] = new BigDecimal(Xs[i]);
|
||
|
bdYs[i] = new BigDecimal(Ys[i]);
|
||
|
}
|
||
|
bdOT = +new Date() - start;
|
||
|
|
||
|
console.log('done. Time taken: %s ms', bdOT || '<1');
|
||
|
|
||
|
showMemoryChange();
|
||
|
|
||
|
|
||
|
// TIME CREATION OF BIGNUMBERS
|
||
|
|
||
|
process.stdout.write(' Creating BigNumbers... ');
|
||
|
|
||
|
start = +new Date();
|
||
|
for (i = 0; i < reps; i++) {
|
||
|
bnXs[i] = new BigNumber(Xs[i]);
|
||
|
bnYs[i] = new BigNumber(Ys[i]);
|
||
|
}
|
||
|
bnOT = +new Date() - start;
|
||
|
|
||
|
console.log('done. Time taken: %s ms', bnOT || '<1');
|
||
|
}
|
||
|
|
||
|
showMemoryChange();
|
||
|
|
||
|
console.log('\n Object creation: %s\n', getFastest(bnOT, bdOT));
|
||
|
|
||
|
|
||
|
// TIME BIGDECIMAL METHOD CALLS
|
||
|
|
||
|
process.stdout.write(pad(' BigDecimal ' + bdM));
|
||
|
|
||
|
if (bdM == 'divide') {
|
||
|
start = +new Date();
|
||
|
while (i--) bdRs[i] = bdXs[i][bdM](bdYs[i], decimalPlaces, rounding);
|
||
|
bdMT = +new Date() - start;
|
||
|
} else {
|
||
|
start = +new Date();
|
||
|
while (i--) bdRs[i] = bdXs[i][bdM](bdYs[i]);
|
||
|
bdMT = +new Date() - start;
|
||
|
}
|
||
|
|
||
|
console.log('done. Time taken: %s ms', bdMT || '<1');
|
||
|
|
||
|
|
||
|
// TIME BIGNUMBER METHOD CALLS
|
||
|
|
||
|
i = reps;
|
||
|
process.stdout.write(pad(' BigNumber ' + bnM));
|
||
|
|
||
|
start = +new Date();
|
||
|
while (i--) bnRs[i] = bnXs[i][bnM](bnYs[i]);
|
||
|
bnMT = +new Date() - start;
|
||
|
|
||
|
console.log('done. Time taken: %s ms', bnMT || '<1');
|
||
|
|
||
|
|
||
|
// TIMINGS SUMMARY
|
||
|
|
||
|
console.log('\n Method calls: %s', getFastest(bnMT, bdMT));
|
||
|
|
||
|
if (!timesEqual) {
|
||
|
console.log('\n Overall: ' +
|
||
|
getFastest((bnOT || 1) + (bnMT || 1), (bdOT || 1) + (bdMT || 1)));
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// CHECK FOR MISMATCHES
|
||
|
|
||
|
process.stdout.write('\n Checking for mismatches... ');
|
||
|
|
||
|
for (i = 0; i < reps; i++) {
|
||
|
|
||
|
bnR = bnRs[i].toString();
|
||
|
bdR = bdRs[i].toPlainString();
|
||
|
|
||
|
// Strip any trailing zeros from non-integer BigDecimals
|
||
|
if (bdR.indexOf('.') != -1) {
|
||
|
bdR = bdR.replace(/\.?0+$/, '');
|
||
|
}
|
||
|
|
||
|
if (bdR !== bnR) {
|
||
|
console.log('breaking on first mismatch (result number %d):' +
|
||
|
'\n\n BigDecimal: %s\n BigNumber: %s', i, bdR, bnR);
|
||
|
console.log('\n x: %s\n y: %s', Xs[i], Ys[i]);
|
||
|
|
||
|
if (bdM == 'divide') {
|
||
|
console.log('\n dp: %d\n r: %d',decimalPlaces, rounding);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (i == reps) {
|
||
|
console.log('done. None found.\n');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|