bigtime-OOM.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. var arg, i, max, method, methodIndex, decimalPlaces,
  2. reps, rounding, start, timesEqual, Xs, Ys,
  3. bdM, bdMT, bdOT, bdRs, bdXs, bdYs,
  4. bnM, bnMT, bnOT, bnRs, bnXs, bnYs,
  5. memoryUsage, showMemory, bnR, bdR,
  6. prevRss, prevHeapUsed, prevHeapTotal,
  7. args = process.argv.splice(2),
  8. BigDecimal = require('./lib/bigdecimal_GWT/bigdecimal').BigDecimal,
  9. BigNumber = require('../bignumber'),
  10. bdMs = ['add', 'subtract', 'multiply', 'divide', 'remainder', 'compareTo', 'pow'],
  11. bnMs1 = ['plus', 'minus', 'multipliedBy', 'dividedBy', 'modulo', 'comparedTo', 'exponentiatedBy'],
  12. bnMs2 = ['', '', '', 'div', 'mod', '', ''],
  13. Ms = [bdMs, bnMs1, bnMs2],
  14. allMs = [].concat.apply([], Ms),
  15. expTotal = 0,
  16. total = 0,
  17. ALWAYS_SHOW_MEMORY = false,
  18. DEFAULT_MAX_DIGITS = 20,
  19. DEFAULT_POW_MAX_DIGITS = 20,
  20. DEFAULT_REPS = 1e4,
  21. DEFAULT_POW_REPS = 1e2,
  22. DEFAULT_PLACES = 20,
  23. MAX_POWER = 50,
  24. getRandom = function (maxDigits) {
  25. var i = 0, z,
  26. // number of digits - 1
  27. n = Math.random() * ( maxDigits || 1 ) | 0,
  28. r = ( Math.random() * 10 | 0 ) + '';
  29. if ( n ) {
  30. if ( z = r === '0' ) {
  31. r += '.';
  32. }
  33. for ( ; i++ < n; r += Math.random() * 10 | 0 ){}
  34. // 20% chance of integer
  35. if ( !z && Math.random() > 0.2 )
  36. r = r.slice( 0, i = ( Math.random() * n | 0 ) + 1 ) + '.' + r.slice(i);
  37. }
  38. // Avoid 'division by zero' error with division and modulo.
  39. if ((bdM == 'divide' || bdM == 'remainder') && parseFloat(r) === 0)
  40. r = ( ( Math.random() * 9 | 0 ) + 1 ) + '';
  41. total += n + 1;
  42. // 50% chance of negative
  43. return Math.random() > 0.5 ? r : '-' + r;
  44. },
  45. pad = function (str) {
  46. str += '... ';
  47. while (str.length < 26) str += ' ';
  48. return str;
  49. },
  50. getFastest = function (bn, bd) {
  51. var r;
  52. if (Math.abs(bn - bd) > 2) {
  53. r = 'Big' + ((bn < bd)
  54. ? 'Number ' + (bn ? parseFloat((bd / bn).toFixed(1)) : bd)
  55. : 'Decimal ' + (bd ? parseFloat((bn / bd).toFixed(1)) : bn)) +
  56. ' times faster';
  57. } else {
  58. timesEqual = 1;
  59. r = 'Times approximately equal';
  60. }
  61. return r;
  62. },
  63. showMemoryChange = function () {
  64. if (showMemory) {
  65. memoryUsage = process.memoryUsage();
  66. var rss = memoryUsage.rss,
  67. heapUsed = memoryUsage.heapUsed,
  68. heapTotal = memoryUsage.heapTotal;
  69. console.log(' Change in memory usage: ' +
  70. ' rss: ' + toKB(rss - prevRss) +
  71. ', hU: ' + toKB(heapUsed - prevHeapUsed) +
  72. ', hT: ' + toKB(heapTotal - prevHeapTotal));
  73. prevRss = rss; prevHeapUsed = heapUsed; prevHeapTotal = heapTotal;
  74. }
  75. },
  76. toKB = function (m) {
  77. return parseFloat((m / 1024).toFixed(1)) + ' KB';
  78. };
  79. // PARSE COMMAND LINE AND SHOW HELP
  80. if (arg = args[0], typeof arg != 'undefined' && !isFinite(arg) &&
  81. allMs.indexOf(arg) == -1 && !/^-*m$/i.test(arg)) {
  82. console.log(
  83. '\n node bigtime-OOM [METHOD] [METHOD CALLS [MAX DIGITS [DECIMAL PLACES]]]\n' +
  84. '\n METHOD: The method to be timed and compared with the automatically' +
  85. '\n chosen corresponding method from BigDecimal or BigNumber\n' +
  86. '\n BigDecimal: add subtract multiply divide remainder compareTo pow' +
  87. '\n BigNumber: plus minus multipliedBy dividedBy modulo comparedTo exponentiatedBy' +
  88. '\n (div mod pow)' +
  89. '\n\n METHOD CALLS: The number of method calls to be timed' +
  90. '\n\n MAX DIGITS: The maximum number of digits of the random ' +
  91. '\n numbers used in the method calls' +
  92. '\n\n DECIMAL PLACES: The number of decimal places used in division' +
  93. '\n (The rounding mode is randomly chosen)' +
  94. '\n\n Default values: METHOD: randomly chosen' +
  95. '\n METHOD CALLS: ' + DEFAULT_REPS +
  96. ' (pow: ' + DEFAULT_POW_REPS + ')' +
  97. '\n MAX DIGITS: ' + DEFAULT_MAX_DIGITS +
  98. ' (pow: ' + DEFAULT_POW_MAX_DIGITS + ')' +
  99. '\n DECIMAL PLACES: ' + DEFAULT_PLACES + '\n' +
  100. '\n E.g.s node bigtime-OOM\n node bigtime-OOM minus' +
  101. '\n node bigtime-OOM add 100000' +
  102. '\n node bigtime-OOM multipliedBy 20000 100' +
  103. '\n node bigtime-OOM dividedBy 100000 50 20' +
  104. '\n node bigtime-OOM 9000' +
  105. '\n node bigtime-OOM 1000000 20\n' +
  106. '\n To show memory usage include an argument m or -m' +
  107. '\n E.g. node bigtime-OOM m add');
  108. } else {
  109. BigNumber.config({
  110. EXPONENTIAL_AT: 1E9,
  111. RANGE: 1E9,
  112. ERRORS: false,
  113. MODULO_MODE: 1,
  114. POW_PRECISION: 10000
  115. });
  116. Number.prototype.toPlainString = Number.prototype.toString;
  117. for (i = 0; i < args.length; i++) {
  118. arg = args[i];
  119. if (isFinite(arg)) {
  120. arg = Math.abs(parseInt(arg));
  121. if (reps == null) {
  122. reps = arg <= 1e10 ? arg : 0;
  123. } else if (max == null) {
  124. max = arg <= 1e6 ? arg : 0;
  125. } else if (decimalPlaces == null) {
  126. decimalPlaces = arg <= 1e6 ? arg : DEFAULT_PLACES;
  127. }
  128. } else if (/^-*m$/i.test(arg)) {
  129. showMemory = true;
  130. } else if (method == null) {
  131. method = arg;
  132. }
  133. }
  134. for (i = 0;
  135. i < Ms.length && (methodIndex = Ms[i].indexOf(method)) == -1;
  136. i++) {}
  137. bnM = methodIndex == -1
  138. ? bnMs1[methodIndex = Math.floor(Math.random() * bdMs.length)]
  139. : (Ms[i][0] == 'add' ? bnMs1 : Ms[i])[methodIndex];
  140. bdM = bdMs[methodIndex];
  141. if (!reps)
  142. reps = bdM == 'pow' ? DEFAULT_POW_REPS : DEFAULT_REPS;
  143. if (!max)
  144. max = bdM == 'pow' ? DEFAULT_POW_MAX_DIGITS : DEFAULT_MAX_DIGITS;
  145. if (decimalPlaces == null)
  146. decimalPlaces = DEFAULT_PLACES;
  147. Xs = [reps], Ys = [reps];
  148. bdXs = [reps], bdYs = [reps], bdRs = [reps];
  149. bnXs = [reps], bnYs = [reps], bnRs = [reps];
  150. showMemory = showMemory || ALWAYS_SHOW_MEMORY;
  151. console.log('\n BigNumber %s vs BigDecimal %s', bnM, bdM);
  152. console.log('\n Method calls: %d', reps);
  153. if (bdM == 'divide') {
  154. rounding = Math.floor(Math.random() * 7);
  155. console.log('\n Decimal places: %d\n Rounding mode: %d', decimalPlaces, rounding);
  156. BigNumber.config({ DECIMAL_PLACES: decimalPlaces, ROUNDING_MODE: rounding });
  157. }
  158. if (showMemory) {
  159. memoryUsage = process.memoryUsage();
  160. console.log(' Memory usage: rss: ' +
  161. toKB(prevRss = memoryUsage.rss) + ', hU: ' +
  162. toKB(prevHeapUsed = memoryUsage.heapUsed) + ', hT: ' +
  163. toKB(prevHeapTotal = memoryUsage.heapTotal));
  164. }
  165. // Create random numbers
  166. // pow: BigDecimal requires JS Number type for exponent argument
  167. if (bdM == 'pow') {
  168. process.stdout.write('\n Creating ' + reps +
  169. ' random numbers (max. digits: ' + max + ')... ');
  170. for (i = 0; i < reps; i++) {
  171. Xs[i] = getRandom(max);
  172. }
  173. console.log('done\n Average number of digits: %d',
  174. ((total / reps) | 0));
  175. process.stdout.write(' Creating ' + reps +
  176. ' random integer exponents (max. value: ' + MAX_POWER + ')... ');
  177. for (i = 0; i < reps; i++) {
  178. bdYs[i] = bnYs[i] = Math.floor(Math.random() * (MAX_POWER + 1));
  179. expTotal += bdYs[i];
  180. }
  181. console.log('done\n Average value: %d', ((expTotal / reps) | 0));
  182. showMemoryChange();
  183. // pow: time creation of BigDecimals
  184. process.stdout.write('\n Creating BigDecimals... ');
  185. start = +new Date();
  186. for (i = 0; i < reps; i++) {
  187. bdXs[i] = new BigDecimal(Xs[i]);
  188. }
  189. bdOT = +new Date() - start;
  190. console.log('done. Time taken: %s ms', bdOT || '<1');
  191. showMemoryChange();
  192. // pow: time creation of BigNumbers
  193. process.stdout.write(' Creating BigNumbers... ');
  194. start = +new Date();
  195. for (i = 0; i < reps; i++) {
  196. bnXs[i] = new BigNumber(Xs[i]);
  197. }
  198. bnOT = +new Date() - start;
  199. console.log('done. Time taken: %s ms', bnOT || '<1');
  200. } else {
  201. process.stdout.write('\n Creating ' + (reps * 2) +
  202. ' random numbers (max. digits: ' + max + ')... ');
  203. for (i = 0; i < reps; i++) {
  204. Xs[i] = getRandom(max);
  205. Ys[i] = getRandom(max);
  206. }
  207. console.log('done\n Average number of digits: %d',
  208. ( total / (reps * 2) ) | 0);
  209. showMemoryChange();
  210. // Time creation of BigDecimals
  211. process.stdout.write('\n Creating BigDecimals... ');
  212. start = +new Date();
  213. for (i = 0; i < reps; i++) {
  214. bdXs[i] = new BigDecimal(Xs[i]);
  215. bdYs[i] = new BigDecimal(Ys[i]);
  216. }
  217. bdOT = +new Date() - start;
  218. console.log('done. Time taken: %s ms', bdOT || '<1');
  219. showMemoryChange();
  220. // Time creation of BigNumbers
  221. process.stdout.write(' Creating BigNumbers... ');
  222. start = +new Date();
  223. for (i = 0; i < reps; i++) {
  224. bnXs[i] = new BigNumber(Xs[i]);
  225. bnYs[i] = new BigNumber(Ys[i]);
  226. }
  227. bnOT = +new Date() - start;
  228. console.log('done. Time taken: %s ms', bnOT || '<1');
  229. }
  230. showMemoryChange();
  231. console.log('\n Object creation: %s\n', getFastest(bnOT, bdOT));
  232. // Time BigDecimal method calls
  233. process.stdout.write(pad(' BigDecimal ' + bdM));
  234. if (bdM == 'divide') {
  235. start = +new Date();
  236. while (i--) bdRs[i] = bdXs[i][bdM](bdYs[i], decimalPlaces, rounding);
  237. bdMT = +new Date() - start;
  238. } else {
  239. start = +new Date();
  240. while (i--) bdRs[i] = bdXs[i][bdM](bdYs[i]);
  241. bdMT = +new Date() - start;
  242. }
  243. console.log('done. Time taken: %s ms', bdMT || '<1');
  244. // Time BigNumber method calls
  245. i = reps;
  246. process.stdout.write(pad(' BigNumber ' + bnM));
  247. start = +new Date();
  248. while (i--) bnRs[i] = bnXs[i][bnM](bnYs[i]);
  249. bnMT = +new Date() - start;
  250. console.log('done. Time taken: %s ms', bnMT || '<1');
  251. // Timings summary
  252. console.log('\n Method calls: %s', getFastest(bnMT, bdMT));
  253. if (!timesEqual) {
  254. console.log('\n Overall: ' +
  255. getFastest((bnOT || 1) + (bnMT || 1), (bdOT || 1) + (bdMT || 1)));
  256. }
  257. // Check for mismatches
  258. process.stdout.write('\n Checking for mismatches... ');
  259. for (i = 0; i < reps; i++) {
  260. bnR = bnRs[i].toString();
  261. bdR = bdRs[i].toPlainString();
  262. // Strip any trailing zeros from non-integer BigDecimals
  263. if (bdR.indexOf('.') != -1) {
  264. bdR = bdR.replace(/\.?0+$/, '');
  265. }
  266. if (bdR !== bnR) {
  267. console.log('breaking on first mismatch (result number %d):' +
  268. '\n\n BigDecimal: %s\n BigNumber: %s', i, bdR, bnR);
  269. console.log('\n x: %s\n y: %s', Xs[i], Ys[i]);
  270. if (bdM == 'divide') {
  271. console.log('\n dp: %d\n r: %d',decimalPlaces, rounding);
  272. }
  273. break;
  274. }
  275. }
  276. if (i == reps) {
  277. console.log('done. None found.\n');
  278. }
  279. }