bigtime.js 11 KB

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