Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

2221 lines
62KB

  1. /*!
  2. * Benchmark.js
  3. * Copyright 2010-2011 Mathias Bynens <http://mths.be/>
  4. * Based on JSLitmus.js, copyright Robert Kieffer <http://broofa.com/>
  5. * Modified by John-David Dalton <http://allyoucanleet.com/>
  6. * Available under MIT license <http://mths.be/mit>
  7. */
  8. (function(window) {
  9. /** Detect DOM0 timeout API (performed at the bottom) */
  10. var HAS_TIMEOUT_API,
  11. /** Detect Adobe AIR environment */
  12. IN_AIR = isClassOf(window.runtime, 'ScriptBridgingProxyObject'),
  13. /** Detect Java environment */
  14. IN_JAVA = isClassOf(window.java, 'JavaPackage'),
  15. /** Used to integrity check compiled tests */
  16. EMBEDDED_UID = +new Date,
  17. /** Used to skip initialization of the Benchmark constructor */
  18. HEADLESS = function() { },
  19. /** Used to avoid hz of Infinity */
  20. CYCLE_DIVISORS = {
  21. '1': 4096,
  22. '2': 512,
  23. '3': 64,
  24. '4': 8,
  25. '5': 0
  26. },
  27. /**
  28. * T-Distribution two-tailed critical values for 95% confidence
  29. * http://www.itl.nist.gov/div898/handbook/eda/section3/eda3672.htm
  30. */
  31. T_DISTRIBUTION = {
  32. '1': 12.706,'2': 4.303, '3': 3.182, '4': 2.776, '5': 2.571, '6': 2.447,
  33. '7': 2.365, '8': 2.306, '9': 2.262, '10': 2.228, '11': 2.201, '12': 2.179,
  34. '13': 2.160, '14': 2.145, '15': 2.131, '16': 2.120, '17': 2.110, '18': 2.101,
  35. '19': 2.093, '20': 2.086, '21': 2.080, '22': 2.074, '23': 2.069, '24': 2.064,
  36. '25': 2.060, '26': 2.056, '27': 2.052, '28': 2.048, '29': 2.045, '30': 2.042,
  37. 'Infinity': 1.960
  38. },
  39. /** Internal cached used by various methods */
  40. cache = {
  41. 'counter': 0
  42. },
  43. /** Used in Benchmark.hasKey() */
  44. hasOwnProperty = cache.hasOwnProperty,
  45. /** Used to convert array-like objects to arrays */
  46. slice = [].slice,
  47. /** Used generically when invoking over queued arrays */
  48. shift = aloClean([].shift),
  49. /** Math shortcuts */
  50. abs = Math.abs,
  51. max = Math.max,
  52. min = Math.min,
  53. pow = Math.pow,
  54. sqrt = Math.sqrt;
  55. /*--------------------------------------------------------------------------*/
  56. /**
  57. * Benchmark constructor.
  58. * @constructor
  59. * @param {String} name A name to identify the benchmark.
  60. * @param {Function} fn The test to benchmark.
  61. * @param {Object} [options={}] Options object.
  62. * @example
  63. *
  64. * // basic usage
  65. * var bench = new Benchmark(fn);
  66. *
  67. * // or using a name first
  68. * var bench = new Benchmark('foo', fn);
  69. *
  70. * // or with options
  71. * var bench = new Benchmark('foo', fn, {
  72. *
  73. * // displayed by Benchmark#toString if `name` is not available
  74. * 'id': 'xyz',
  75. *
  76. * // called when the benchmark starts running
  77. * 'onStart': onStart,
  78. *
  79. * // called after each run cycle
  80. * 'onCycle': onCycle,
  81. *
  82. * // called when aborted
  83. * 'onAbort': onAbort,
  84. *
  85. * // called when a test errors
  86. * 'onError': onError,
  87. *
  88. * // called when reset
  89. * 'onReset': onReset,
  90. *
  91. * // called when the benchmark completes running
  92. * 'onComplete': onComplete,
  93. *
  94. * // compiled/called before the test loop
  95. * 'setup': setup,
  96. *
  97. * // compiled/called after the test loop
  98. * 'teardown': teardown
  99. * });
  100. *
  101. * // or options only
  102. * var bench = new Benchmark({
  103. *
  104. * // benchmark name
  105. * 'name': 'foo',
  106. *
  107. * // benchmark test function
  108. * 'fn': fn
  109. * });
  110. *
  111. * // a test's `this` binding is set to the benchmark instance
  112. * var bench = new Benchmark('foo', function() {
  113. * 'My name is '.concat(this.name); // My name is foo
  114. * });
  115. */
  116. function Benchmark(name, fn, options) {
  117. // juggle arguments
  118. var me = this;
  119. if (isClassOf(name, 'Object')) {
  120. // 1 argument
  121. options = name;
  122. }
  123. else if (isClassOf(name, 'Function')) {
  124. // 2 arguments
  125. options = fn;
  126. fn = name;
  127. }
  128. else {
  129. // 3 arguments
  130. me.name = name;
  131. }
  132. // initialize if not headless
  133. if (name != HEADLESS) {
  134. setOptions(me, options);
  135. fn = me.fn || (me.fn = fn);
  136. fn.uid || (fn.uid = ++cache.counter);
  137. me.created = +new Date;
  138. me.stats = extend({ }, me.stats);
  139. me.times = extend({ }, me.times);
  140. }
  141. }
  142. /**
  143. * Suite constructor.
  144. * @constructor
  145. * @member Benchmark
  146. * @param {String} name A name to identify the suite.
  147. * @param {Object} [options={}] Options object.
  148. * @example
  149. *
  150. * // basic usage
  151. * var suite = new Benchmark.Suite;
  152. *
  153. * // or using a name first
  154. * var suite = new Benchmark.Suite('foo');
  155. *
  156. * // or with options
  157. * var suite = new Benchmark.Suite('foo', {
  158. *
  159. * // called when the suite starts running
  160. * 'onStart': onStart,
  161. *
  162. * // called between running benchmarks
  163. * 'onCycle': onCycle,
  164. *
  165. * // called when aborted
  166. * 'onAbort': onAbort,
  167. *
  168. * // called when a test errors
  169. * 'onError': onError,
  170. *
  171. * // called when reset
  172. * 'onReset': onReset,
  173. *
  174. * // called when the suite completes running
  175. * 'onComplete': onComplete
  176. * });
  177. */
  178. function Suite(name, options) {
  179. // juggle arguments
  180. var me = this;
  181. if (isClassOf(name, 'Object')) {
  182. options = name;
  183. } else {
  184. me.name = name;
  185. }
  186. setOptions(me, options);
  187. }
  188. /*--------------------------------------------------------------------------*/
  189. /**
  190. * Wraps an array function to ensure array-like-objects (ALO) are empty when their length is `0`.
  191. * @private
  192. * @param {Function} fn The array function to be wrapped.
  193. * @returns {Function} The new function.
  194. */
  195. function aloClean(fn) {
  196. return function() {
  197. var me = this,
  198. result = fn.apply(me, arguments);
  199. // fixes IE<9 and IE8 compatibility mode bugs
  200. if (!me.length) {
  201. delete me[0];
  202. }
  203. return result;
  204. };
  205. }
  206. /**
  207. * Executes a function asynchronously or synchronously.
  208. * @private
  209. * @param {Object} me The benchmark instance passed to `fn`.
  210. * @param {Function} fn Function to be executed.
  211. * @param {Boolean} [async=false] Flag to run asynchronously.
  212. */
  213. function call(me, fn, async) {
  214. // only attempt asynchronous calls if supported
  215. if (async && HAS_TIMEOUT_API) {
  216. me.timerId = setTimeout(function() {
  217. delete me.timerId;
  218. fn();
  219. }, me.CYCLE_DELAY * 1e3);
  220. }
  221. else {
  222. fn();
  223. }
  224. }
  225. /**
  226. * Clocks the time taken to execute a test per cycle (secs).
  227. * @private
  228. * @param {Object} me The benchmark instance.
  229. * @returns {Number} The time taken.
  230. */
  231. function clock() {
  232. var applet,
  233. args,
  234. fallback,
  235. proto = Benchmark.prototype,
  236. timers = [],
  237. timerNS = Date,
  238. msRes = getRes('ms'),
  239. timer = { 'ns': timerNS, 'res': max(0.0015, msRes), 'unit': 'ms' },
  240. code = 'var r$,s$,m$=this,i$=m$.count,f$=m$.fn;#{setup}#{start};while(i$--){|}#{end};#{teardown}return{time:r$,uid:"$"}|m$.teardown&&m$.teardown();|f$()|m$.setup&&m$.setup();|n$';
  241. clock = function(me) {
  242. var result,
  243. fn = me.fn,
  244. compiled = fn.compiled,
  245. count = me.count;
  246. if (applet) {
  247. // repair nanosecond timer
  248. try {
  249. timerNS.nanoTime();
  250. } catch(e) {
  251. // use non-element to avoid issues with libs that augment them
  252. timerNS = new applet.Packages.nano;
  253. }
  254. }
  255. if (compiled == null || compiled) {
  256. try {
  257. if (!compiled) {
  258. // insert test body into the while-loop
  259. compiled = createFunction(args,
  260. interpolate(code[0], { 'setup': getSource(me.setup) }) +
  261. getSource(fn) +
  262. interpolate(code[1], { 'teardown': getSource(me.teardown) })
  263. );
  264. // determine if compiled code is exited early, usually by a rogue
  265. // return statement, by checking for a return object with the uid
  266. me.count = 1;
  267. compiled = fn.compiled = compiled.call(me, timerNS).uid == EMBEDDED_UID && compiled;
  268. me.count = count;
  269. }
  270. if (compiled) {
  271. result = compiled.call(me, timerNS).time;
  272. }
  273. } catch(e) {
  274. me.count = count;
  275. compiled = fn.compiled = false;
  276. }
  277. }
  278. // fallback to simple while-loop when compiled is false
  279. if (!compiled) {
  280. result = fallback.call(me, timerNS).time;
  281. }
  282. return result;
  283. };
  284. function getRes(unit) {
  285. var measured,
  286. start,
  287. count = 30,
  288. divisor = 1e3,
  289. sample = [];
  290. // get average smallest measurable time
  291. while (count--) {
  292. if (unit == 'us') {
  293. divisor = 1e6;
  294. if (timerNS.stop) {
  295. timerNS.start();
  296. while(!(measured = timerNS.microseconds()));
  297. } else {
  298. start = timerNS();
  299. while(!(measured = timerNS() - start));
  300. }
  301. }
  302. else if (unit == 'ns') {
  303. divisor = 1e9;
  304. start = timerNS.nanoTime();
  305. while(!(measured = timerNS.nanoTime() - start));
  306. }
  307. else {
  308. start = new timerNS;
  309. while(!(measured = new timerNS - start));
  310. }
  311. // check for broken timers (nanoTime may have issues)
  312. // http://alivebutsleepy.srnet.cz/unreliable-system-nanotime/
  313. if (measured > 0) {
  314. sample.push(measured);
  315. } else {
  316. sample.push(Infinity);
  317. break;
  318. }
  319. }
  320. // convert to seconds
  321. return getMean(sample) / divisor;
  322. }
  323. // detect nanosecond support from a Java applet
  324. each(window.document && document.applets || [], function(element) {
  325. if (timerNS = applet = 'nanoTime' in element && element) {
  326. return false;
  327. }
  328. });
  329. // or the exposed Java API
  330. // http://download.oracle.com/javase/6/docs/api/java/lang/System.html#nanoTime()
  331. try {
  332. timerNS = java.lang.System;
  333. } catch(e) { }
  334. // check type in case Safari returns an object instead of a number
  335. try {
  336. if (typeof timerNS.nanoTime() == 'number') {
  337. timers.push({ 'ns': timerNS, 'res': getRes('ns'), 'unit': 'ns' });
  338. }
  339. } catch(e) { }
  340. // detect Chrome's microsecond timer:
  341. // enable benchmarking via the --enable-benchmarking command
  342. // line switch in at least Chrome 7 to use chrome.Interval
  343. try {
  344. if (timerNS = new (window.chrome || window.chromium).Interval) {
  345. timers.push({ 'ns': timerNS, 'res': getRes('us'), 'unit': 'us' });
  346. }
  347. } catch(e) { }
  348. // detect Node's microtime module:
  349. // npm install microtime
  350. try {
  351. if (timerNS = typeof require == 'function' && !window.require && require('microtime').now) {
  352. timers.push({ 'ns': timerNS, 'res': getRes('us'), 'unit': 'us' });
  353. }
  354. } catch(e) { }
  355. // pick timer with highest resolution
  356. timerNS = (timer = reduce(timers, function(timer, other) {
  357. return other.res < timer.res ? other : timer;
  358. }, timer)).ns;
  359. // restore ms res
  360. if (timer.unit == 'ms') {
  361. timer.res = msRes;
  362. }
  363. // remove unused applet
  364. if (timer.unit != 'ns' && applet) {
  365. applet.parentNode.removeChild(applet);
  366. applet = null;
  367. }
  368. // error if there are no working timers
  369. if (timer.res == Infinity) {
  370. throw new Error('Benchmark.js was unable to find a working timer.');
  371. }
  372. // use API of chosen timer
  373. if (timer.unit == 'ns') {
  374. code = interpolate(code, {
  375. 'start': 's$=n$.nanoTime()',
  376. 'end': 'r$=(n$.nanoTime()-s$)/1e9'
  377. });
  378. }
  379. else if (timer.unit == 'us') {
  380. code = interpolate(code, timerNS.stop ? {
  381. 'start': 's$=n$.start()',
  382. 'end': 'r$=n$.microseconds()/1e6'
  383. } : {
  384. 'start': 's$=n$()',
  385. 'end': 'r$=(n$()-s$)/1e6'
  386. });
  387. }
  388. else {
  389. code = interpolate(code, {
  390. 'start': 's$=new n$',
  391. 'end': 'r$=(new n$-s$)/1e3'
  392. });
  393. }
  394. // inject uid into variable names to avoid collisions with
  395. // embedded tests and create non-embedding fallback
  396. code = code.replace(/\$/g, EMBEDDED_UID).split('|');
  397. args = code.pop();
  398. fallback = createFunction(args,
  399. interpolate(code[0], { 'setup': code.pop() }) +
  400. code.pop() +
  401. interpolate(code[1], { 'teardown': code.pop() })
  402. );
  403. // resolve time to achieve a percent uncertainty of 1%
  404. proto.MIN_TIME || (proto.MIN_TIME = max(timer.res / 2 / 0.01, 0.05));
  405. return clock.apply(null, arguments);
  406. }
  407. /**
  408. * Creates a function from the given arguments string and body.
  409. * @private
  410. * @param {String} args The comma separated function arguments.
  411. * @param {String} body The function body.
  412. * @returns {Function} The new function.
  413. */
  414. function createFunction() {
  415. var scripts,
  416. prop = 'c' + EMBEDDED_UID;
  417. createFunction = function(args, body) {
  418. var parent = scripts[0].parentNode,
  419. script = document.createElement('script');
  420. script.appendChild(document.createTextNode('Benchmark.' + prop + '=function(' + args + '){' + body + '}'));
  421. parent.removeChild(parent.insertBefore(script, scripts[0]));
  422. return [Benchmark[prop], delete Benchmark[prop]][0];
  423. };
  424. // fix JaegerMonkey bug
  425. // http://bugzil.la/639720
  426. try {
  427. scripts = document.getElementsByTagName('script');
  428. createFunction = createFunction('', 'return ' + EMBEDDED_UID)() == EMBEDDED_UID && createFunction;
  429. } catch (e) {
  430. createFunction = false;
  431. }
  432. createFunction || (createFunction = Function);
  433. return createFunction.apply(null, arguments);
  434. }
  435. /**
  436. * Wraps a function and passes `this` to the original function as the first argument.
  437. * @private
  438. * @param {Function} fn The function to be wrapped.
  439. * @returns {Function} The new function.
  440. */
  441. function methodize(fn) {
  442. return function() {
  443. return fn.apply(this, [this].concat(slice.call(arguments)));
  444. };
  445. }
  446. /**
  447. * Gets the critical value for the specified degrees of freedom.
  448. * @private
  449. * @param {Number} df The degrees of freedom.
  450. * @returns {Number} The critical value.
  451. */
  452. function getCriticalValue(df) {
  453. return T_DISTRIBUTION[Math.round(df) || 1] || T_DISTRIBUTION.Infinity;
  454. }
  455. /**
  456. * Computes the arithmetic mean of a sample.
  457. * @private
  458. * @param {Array} sample The sample.
  459. * @returns {Number} The mean.
  460. */
  461. function getMean(sample) {
  462. return reduce(sample, function(sum, x) {
  463. return sum + x;
  464. }, 0) / sample.length || 0;
  465. }
  466. /**
  467. * Gets the source code of a function.
  468. * @private
  469. * @param {Function} fn The function.
  470. * @returns {String} The function's source code.
  471. */
  472. function getSource(fn) {
  473. return trim((/^[^{]+{([\s\S]*)}\s*$/.exec(fn) || 0)[1] || '')
  474. .replace(/([^\n])$/, '$1\n');
  475. }
  476. /**
  477. * Sets the options of a benchmark.
  478. * @private
  479. * @param {Object} me The benchmark instance.
  480. * @param {Object} [options={}] Options object.
  481. */
  482. function setOptions(me, options) {
  483. options = extend(extend({ }, me.constructor.options), options);
  484. me.options = each(options, function(value, key) {
  485. // add event listeners
  486. if (/^on[A-Z]/.test(key)) {
  487. each(key.split(' '), function(key) {
  488. me.on(key.slice(2).toLowerCase(), value);
  489. });
  490. } else {
  491. me[key] = value;
  492. }
  493. });
  494. }
  495. /*--------------------------------------------------------------------------*/
  496. /**
  497. * A bare-bones `Array#forEach`/`for-in` own property solution.
  498. * Callbacks may terminate the loop by explicitly returning `false`.
  499. * @static
  500. * @member Benchmark
  501. * @param {Array|Object} object The object to iterate over.
  502. * @param {Function} callback The function called per iteration.
  503. * @returns {Array|Object} Returns the object iterated over.
  504. */
  505. function each(object, callback) {
  506. var i = -1,
  507. length = object.length;
  508. if (hasKey(object, 'length') && length > -1 && length < 4294967296) {
  509. while (++i < length) {
  510. if (i in object && callback(object[i], i, object) === false) {
  511. break;
  512. }
  513. }
  514. } else {
  515. for (i in object) {
  516. if (hasKey(object, i) && callback(object[i], i, object) === false) {
  517. break;
  518. }
  519. }
  520. }
  521. return object;
  522. }
  523. /**
  524. * Copies own/inherited properties of a source object to the destination object.
  525. * @static
  526. * @member Benchmark
  527. * @param {Object} destination The destination object.
  528. * @param {Object} [source={}] The source object.
  529. * @returns {Object} The destination object.
  530. */
  531. function extend(destination, source) {
  532. source || (source = { });
  533. for (var key in source) {
  534. destination[key] = source[key];
  535. }
  536. return destination;
  537. }
  538. /**
  539. * A generic bare-bones `Array#filter` solution.
  540. * @static
  541. * @member Benchmark
  542. * @param {Array} array The array to iterate over.
  543. * @param {Function|String} callback The function/alias called per iteration.
  544. * @returns {Array} A new array of values that passed callback filter.
  545. * @example
  546. *
  547. * // get odd numbers
  548. * Benchmark.filter([1, 2, 3, 4, 5], function(n) {
  549. * return n % 2;
  550. * }); // -> [1, 3, 5];
  551. *
  552. * // get fastest benchmarks
  553. * Benchmark.filter(benches, 'fastest');
  554. *
  555. * // get slowest benchmarks
  556. * Benchmark.filter(benches, 'slowest');
  557. *
  558. * // get benchmarks that completed without erroring
  559. * Benchmark.filter(benches, 'successful');
  560. */
  561. function filter(array, callback) {
  562. var result;
  563. if (callback == 'successful') {
  564. // callback to exclude errored or unrun benchmarks
  565. callback = function(bench) { return bench.cycles; };
  566. }
  567. else if (/^(?:fast|slow)est$/.test(callback)) {
  568. // get successful, sort by period + margin of error, and filter fastest/slowest
  569. result = filter(array, 'successful').sort(function(a, b) {
  570. a = a.stats;
  571. b = b.stats;
  572. return (a.mean + a.ME > b.mean + b.ME ? 1 : -1) * (callback == 'fastest' ? 1 : -1);
  573. });
  574. result = filter(result, function(bench) {
  575. return !result[0].compare(bench);
  576. });
  577. }
  578. return result || reduce(array, function(result, value, index) {
  579. return callback(value, index, array) ? result.push(value) && result : result;
  580. }, []);
  581. }
  582. /**
  583. * Converts a number to a more readable comma-separated string representation.
  584. * @static
  585. * @member Benchmark
  586. * @param {Number} number The number to convert.
  587. * @returns {String} The more readable string representation.
  588. */
  589. function formatNumber(number) {
  590. number = String(number).split('.');
  591. return number[0].replace(/(?=(?:\d{3})+$)(?!\b)/g, ',') + (number[1] ? '.' + number[1] : '');
  592. }
  593. /**
  594. * Checks if an object has the specified key as a direct property.
  595. * @static
  596. * @member Benchmark
  597. * @param {Object} object The object to check.
  598. * @param {String} key The key to check for.
  599. * @returns {Boolean} Returns `true` if key is a direct property, else `false`.
  600. */
  601. function hasKey(object, key) {
  602. var result,
  603. parent = (object.constructor || Object).prototype;
  604. // for modern browsers
  605. object = Object(object);
  606. if (isClassOf(hasOwnProperty, 'Function')) {
  607. result = hasOwnProperty.call(object, key);
  608. }
  609. // for Safari 2
  610. else if (cache.__proto__ == Object.prototype) {
  611. object.__proto__ = [object.__proto__, object.__proto__ = null, result = key in object][0];
  612. }
  613. // for others (not as accurate)
  614. else {
  615. result = key in object && !(key in parent && object[key] === parent[key]);
  616. }
  617. return result;
  618. }
  619. /**
  620. * A generic bare-bones `Array#indexOf` solution.
  621. * @static
  622. * @member Benchmark
  623. * @param {Array} array The array to iterate over.
  624. * @param {Mixed} value The value to search for.
  625. * @returns {Number} The index of the matched value or `-1`.
  626. */
  627. function indexOf(array, value) {
  628. var result = -1;
  629. each(array, function(v, i) {
  630. if (v === value) {
  631. result = i;
  632. return false;
  633. }
  634. });
  635. return result;
  636. }
  637. /**
  638. * Invokes a method on all items in an array.
  639. * @static
  640. * @member Benchmark
  641. * @param {Array} benches Array of benchmarks to iterate over.
  642. * @param {String|Object} name The name of the method to invoke OR options object.
  643. * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with.
  644. * @returns {Array} A new array of values returned from each method invoked.
  645. * @example
  646. *
  647. * // invoke `reset` on all benchmarks
  648. * Benchmark.invoke(benches, 'reset');
  649. *
  650. * // invoke `emit` with arguments
  651. * Benchmark.invoke(benches, 'emit', 'complete', listener);
  652. *
  653. * // invoke `run(true)`, treat benchmarks as a queue, and register invoke callbacks
  654. * Benchmark.invoke(benches, {
  655. *
  656. * // invoke the `run` method
  657. * 'name': 'run',
  658. *
  659. * // pass a single argument
  660. * 'args': true,
  661. *
  662. * // treat as queue, removing benchmarks from front of `benches` until empty
  663. * 'queued': true,
  664. *
  665. * // called before any benchmarks have been invoked.
  666. * 'onStart': onStart,
  667. *
  668. * // called between invoking benchmarks
  669. * 'onCycle': onCycle,
  670. *
  671. * // called after all benchmarks have been invoked.
  672. * 'onComplete': onComplete
  673. * });
  674. */
  675. function invoke(benches, name) {
  676. var args,
  677. async,
  678. bench,
  679. queued,
  680. i = 0,
  681. options = { 'onStart': noop, 'onCycle': noop, 'onComplete': noop },
  682. result = slice.call(benches, 0);
  683. function execute() {
  684. var listeners;
  685. if (async) {
  686. // use `next` as a listener
  687. bench.on('complete', next);
  688. listeners = bench.events['complete'];
  689. listeners.splice(0, 0, listeners.pop());
  690. }
  691. // execute method
  692. result[i] = bench[name].apply(bench, args);
  693. // if synchronous return true until finished
  694. return async || next();
  695. }
  696. function next() {
  697. var last = bench;
  698. bench = false;
  699. if (async) {
  700. last.removeListener('complete', next);
  701. last.emit('complete');
  702. }
  703. // choose next benchmark if not exiting early
  704. if (options.onCycle.call(benches, last) !== false) {
  705. if (queued) {
  706. // use generic shift
  707. shift.call(benches);
  708. bench = benches[0];
  709. } else {
  710. bench = benches[++i];
  711. }
  712. }
  713. if (bench) {
  714. if (async) {
  715. call(bench, execute, async);
  716. } else {
  717. return true;
  718. }
  719. } else {
  720. options.onComplete.call(benches, last);
  721. }
  722. // when async the `return false` will cancel the rest of the "complete"
  723. // listeners because they were called above and when synchronous it will
  724. // end the while-loop
  725. return false;
  726. }
  727. // juggle arguments
  728. if (isClassOf(name, 'String')) {
  729. args = slice.call(arguments, 2);
  730. } else {
  731. options = extend(options, name);
  732. name = options.name;
  733. args = isArray(args = 'args' in options ? options.args : []) ? args : [args];
  734. queued = options.queued;
  735. }
  736. // async for use with Benchmark#run only
  737. if (name == 'run') {
  738. async = (args[0] == null ? Benchmark.prototype.DEFAULT_ASYNC :
  739. args[0]) && HAS_TIMEOUT_API;
  740. }
  741. // start iterating over the array
  742. if (bench = benches[0]) {
  743. options.onStart.call(benches, bench);
  744. if (async) {
  745. call(bench, execute, async);
  746. } else {
  747. result.length = 0;
  748. while (execute());
  749. }
  750. }
  751. return result;
  752. }
  753. /**
  754. * Modify a string by replacing named tokens with matching object property values.
  755. * @static
  756. * @member Benchmark
  757. * @param {String} string The string to modify.
  758. * @param {Object} object The template object.
  759. * @returns {String} The modified string.
  760. * @example
  761. *
  762. * Benchmark.interpolate('#{greet} #{who}!', {
  763. * 'greet': 'Hello',
  764. * 'who': 'world'
  765. * }); // -> 'Hello world!'
  766. */
  767. function interpolate(string, object) {
  768. string = string == null ? '' : string;
  769. each(object || { }, function(value, key) {
  770. string = string.replace(RegExp('#\\{' + key + '\\}', 'g'), String(value));
  771. });
  772. return string;
  773. }
  774. /**
  775. * Determines if the given value is an array.
  776. * @static
  777. * @member Benchmark
  778. * @param {Mixed} value The value to check.
  779. * @returns {Boolean} Returns `true` if value is an array, else `false`.
  780. */
  781. function isArray(value) {
  782. return isClassOf(value, 'Array');
  783. }
  784. /**
  785. * Checks if an object is of the specified class.
  786. * @static
  787. * @member Benchmark
  788. * @param {Object} object The object.
  789. * @param {String} name The name of the class.
  790. * @returns {Boolean} Returns `true` if of the class, else `false`.
  791. */
  792. function isClassOf(object, name) {
  793. return object != null && {}.toString.call(object).slice(8, -1) == name;
  794. }
  795. /**
  796. * Host objects can return type values that are different from their actual
  797. * data type. The objects we are concerned with usually return non-primitive
  798. * types of object, function, or unknown.
  799. * @static
  800. * @member Benchmark
  801. * @param {Mixed} object The owner of the property.
  802. * @param {String} property The property to check.
  803. * @returns {Boolean} Returns `true` if the property value is a non-primitive, else `false`.
  804. */
  805. function isHostType(object, property) {
  806. return !/^(?:boolean|number|string|undefined)$/
  807. .test(typeof object[property]) && !!object[property];
  808. }
  809. /**
  810. * Creates a string of joined array values or object key-value pairs.
  811. * @static
  812. * @member Benchmark
  813. * @param {Array|Object} object The object to operate on.
  814. * @param {String} [separator1=','] The separator used between key-value pairs.
  815. * @param {String} [separator2=': '] The separator used between keys and values.
  816. * @returns {String} The joined result.
  817. */
  818. function join(object, separator1, separator2) {
  819. var pairs = [];
  820. if (isArray(object)) {
  821. pairs = object;
  822. }
  823. else {
  824. separator2 || (separator2 = ': ');
  825. each(object, function(value, key) {
  826. pairs.push(key + separator2 + value);
  827. });
  828. }
  829. return pairs.join(separator1 || ',');
  830. }
  831. /**
  832. * A generic bare-bones `Array#map` solution.
  833. * @static
  834. * @member Benchmark
  835. * @param {Array} array The array to iterate over.
  836. * @param {Function} callback The function called per iteration.
  837. * @returns {Array} A new array of values returned by the callback.
  838. */
  839. function map(array, callback) {
  840. return reduce(array, function(result, value, index) {
  841. result.push(callback(value, index, array));
  842. return result;
  843. }, []);
  844. }
  845. /**
  846. * A no-operation function.
  847. * @static
  848. * @member Benchmark
  849. */
  850. function noop() {
  851. // no operation performed
  852. }
  853. /**
  854. * Retrieves the value of a specified property from all items in an array.
  855. * @static
  856. * @member Benchmark
  857. * @param {Array} array The array to iterate over.
  858. * @param {String} property The property to pluck.
  859. * @returns {Array} A new array of property values.
  860. */
  861. function pluck(array, property) {
  862. return map(array, function(object) {
  863. return object[property];
  864. });
  865. }
  866. /**
  867. * A generic bare-bones `Array#reduce` solution.
  868. * @static
  869. * @member Benchmark
  870. * @param {Array} array The array to iterate over.
  871. * @param {Function} callback The function called per iteration.
  872. * @param {Mixed} accumulator Initial value of the accumulator.
  873. * @returns {Mixed} The accumulator.
  874. */
  875. function reduce(array, callback, accumulator) {
  876. each(array, function(value, index) {
  877. accumulator = callback(accumulator, value, index, array);
  878. });
  879. return accumulator;
  880. }
  881. /**
  882. * A generic bare-bones `String#trim` solution.
  883. * @static
  884. * @member Benchmark
  885. * @param {String} string The string to trim.
  886. * @returns {String} The trimmed string.
  887. */
  888. function trim(string) {
  889. return String(string).replace(/^\s+/, '').replace(/\s+$/, '');
  890. }
  891. /*--------------------------------------------------------------------------*/
  892. /**
  893. * Aborts all benchmarks in the suite.
  894. * @name abort
  895. * @member Benchmark.Suite
  896. * @returns {Object} The suite instance.
  897. */
  898. function abortSuite() {
  899. var me = this;
  900. me.aborted = true;
  901. me.running = false;
  902. invoke(me, 'abort');
  903. me.emit('abort');
  904. return me;
  905. }
  906. /**
  907. * Adds a test to the benchmark suite.
  908. * @member Benchmark.Suite
  909. * @param {String} name A name to identify the benchmark.
  910. * @param {Function} fn The test to benchmark.
  911. * @param {Object} [options={}] Options object.
  912. * @returns {Object} The benchmark instance.
  913. * @example
  914. *
  915. * // basic usage
  916. * suite.add(fn);
  917. *
  918. * // or using a name first
  919. * suite.add('foo', fn);
  920. *
  921. * // or with options
  922. * suite.add('foo', fn, {
  923. * 'onCycle': onCycle,
  924. * 'onComplete': onComplete
  925. * });
  926. */
  927. function add(name, fn, options) {
  928. var me = this,
  929. bench = new Benchmark(HEADLESS);
  930. Benchmark.call(bench, name, fn, options);
  931. me.push(bench);
  932. me.emit('add', bench);
  933. return me;
  934. }
  935. /**
  936. * Creates a new suite with cloned benchmarks.
  937. * @name clone
  938. * @member Benchmark.Suite
  939. * @param {Object} options Options object to overwrite cloned options.
  940. * @returns {Object} The new suite instance.
  941. */
  942. function cloneSuite(options) {
  943. var me = this,
  944. result = new me.constructor(extend(extend({ }, me.options), options));
  945. // copy own properties
  946. each(me, function(value, key) {
  947. if (!hasKey(result, key)) {
  948. result[key] = /^\d+$/.test(key) ? value.clone() : value;
  949. }
  950. });
  951. return result.reset();
  952. }
  953. /**
  954. * A bare-bones `Array#filter` solution.
  955. * @name filter
  956. * @member Benchmark.Suite
  957. * @param {Function|String} callback The function/alias called per iteration.
  958. * @returns {Object} A new suite of benchmarks that passed callback filter.
  959. */
  960. function filterSuite(callback) {
  961. var me = this,
  962. result = new me.constructor;
  963. result.push.apply(result, filter(me, callback));
  964. return result;
  965. }
  966. /**
  967. * Resets all benchmarks in the suite.
  968. * @name reset
  969. * @member Benchmark.Suite
  970. * @returns {Object} The suite instance.
  971. */
  972. function resetSuite() {
  973. var me = this;
  974. me.aborted = me.running = false;
  975. invoke(me, 'reset');
  976. me.emit('reset');
  977. return me;
  978. }
  979. /**
  980. * Runs the suite.
  981. * @name run
  982. * @member Benchmark.Suite
  983. * @param {Boolean} [async=false] Flag to run asynchronously.
  984. * @param {Boolean} [queued=false] Flag to treat benchmarks as a queue.
  985. * @returns {Object} The suite instance.
  986. */
  987. function runSuite(async, queued) {
  988. var me = this;
  989. me.reset();
  990. me.running = true;
  991. invoke(me, {
  992. 'name': 'run',
  993. 'args': async,
  994. 'queued': queued,
  995. 'onStart': function(bench) {
  996. me.emit('start', bench);
  997. },
  998. 'onCycle': function(bench) {
  999. if (bench.error) {
  1000. me.emit('error', bench);
  1001. }
  1002. return !me.aborted && me.emit('cycle', bench);
  1003. },
  1004. 'onComplete': function(bench) {
  1005. me.running = false;
  1006. me.emit('complete', bench);
  1007. }
  1008. });
  1009. return me;
  1010. }
  1011. /*--------------------------------------------------------------------------*/
  1012. /**
  1013. * Registers a single listener of a specified event type.
  1014. * @member Benchmark, Benchmark.Suite
  1015. * @param {String} type The event type.
  1016. * @param {Function} listener The function called when the event occurs.
  1017. * @returns {Object} The benchmark instance.
  1018. * @example
  1019. *
  1020. * // basic usage
  1021. * bench.addListener('cycle', listener);
  1022. *
  1023. * // register a listener for multiple event types
  1024. * bench.addListener('start cycle', listener);
  1025. */
  1026. function addListener(type, listener) {
  1027. var me = this,
  1028. events = me.events || (me.events = { });
  1029. each(type.split(' '), function(type) {
  1030. (events[type] || (events[type] = [])).push(listener);
  1031. });
  1032. return me;
  1033. }
  1034. /**
  1035. * Executes all registered listeners of a specified event type.
  1036. * @member Benchmark, Benchmark.Suite
  1037. * @param {String} type The event type.
  1038. */
  1039. function emit(type) {
  1040. var me = this,
  1041. args = slice.call(arguments, 1),
  1042. events = me.events,
  1043. listeners = events && events[type] || [],
  1044. successful = true;
  1045. each(listeners, function(listener) {
  1046. if (listener.apply(me, args) === false) {
  1047. successful = false;
  1048. return successful;
  1049. }
  1050. });
  1051. return successful;
  1052. }
  1053. /**
  1054. * Unregisters a single listener of a specified event type.
  1055. * @member Benchmark, Benchmark.Suite
  1056. * @param {String} type The event type.
  1057. * @param {Function} listener The function to unregister.
  1058. * @returns {Object} The benchmark instance.
  1059. * @example
  1060. *
  1061. * // basic usage
  1062. * bench.removeListener('cycle', listener);
  1063. *
  1064. * // unregister a listener for multiple event types
  1065. * bench.removeListener('start cycle', listener);
  1066. */
  1067. function removeListener(type, listener) {
  1068. var me = this,
  1069. events = me.events;
  1070. each(type.split(' '), function(type) {
  1071. var listeners = events && events[type] || [],
  1072. index = indexOf(listeners, listener);
  1073. if (index > -1) {
  1074. listeners.splice(index, 1);
  1075. }
  1076. });
  1077. return me;
  1078. }
  1079. /**
  1080. * Unregisters all listeners of a specified event type.
  1081. * @member Benchmark, Benchmark.Suite
  1082. * @param {String} type The event type.
  1083. * @returns {Object} The benchmark instance.
  1084. * @example
  1085. *
  1086. * // basic usage
  1087. * bench.removeAllListeners('cycle');
  1088. *
  1089. * // unregister all listeners for multiple event types
  1090. * bench.removeListener('start cycle');
  1091. */
  1092. function removeAllListeners(type) {
  1093. var me = this,
  1094. events = me.events;
  1095. each(type.split(' '), function(type) {
  1096. (events && events[type] || []).length = 0;
  1097. });
  1098. return me;
  1099. }
  1100. /*--------------------------------------------------------------------------*/
  1101. /**
  1102. * Aborts the benchmark without recording times.
  1103. * @member Benchmark
  1104. * @returns {Object} The benchmark instance.
  1105. */
  1106. function abort() {
  1107. var me = this;
  1108. if (me.running) {
  1109. if (me.timerId && HAS_TIMEOUT_API) {
  1110. clearTimeout(me.timerId);
  1111. delete me.timerId;
  1112. }
  1113. // set running as NaN so reset() will detect it as falsey and *not* call abort(),
  1114. // but *will* detect it as a change and fire the onReset() callback
  1115. me.running = NaN;
  1116. me.reset();
  1117. me.aborted = true;
  1118. me.emit('abort');
  1119. }
  1120. return me;
  1121. }
  1122. /**
  1123. * Creates a new benchmark using the same test and options.
  1124. * @member Benchmark
  1125. * @param {Object} options Options object to overwrite cloned options.
  1126. * @returns {Object} The new benchmark instance.
  1127. * @example
  1128. *
  1129. * var bizarro = bench.clone({
  1130. * 'name': 'doppelganger'
  1131. * });
  1132. */
  1133. function clone(options) {
  1134. var me = this,
  1135. result = new me.constructor(me.fn, extend(extend({ }, me.options), options));
  1136. // copy own properties
  1137. each(me, function(value, key) {
  1138. if (!hasKey(result, key)) {
  1139. result[key] = value;
  1140. }
  1141. });
  1142. return result.reset();
  1143. }
  1144. /**
  1145. * Determines if the benchmark's period is smaller than another.
  1146. * @member Benchmark
  1147. * @param {Object} other The benchmark to compare.
  1148. * @returns {Number} Returns `1` if smaller, `-1` if larger, and `0` if indeterminate.
  1149. */
  1150. function compare(other) {
  1151. // unpaired two-sample t-test assuming equal variance
  1152. // http://en.wikipedia.org/wiki/Student's_t-test
  1153. // http://www.chem.utoronto.ca/coursenotes/analsci/StatsTutorial/12tailed.html
  1154. var a = this.stats,
  1155. b = other.stats,
  1156. df = a.size + b.size - 2,
  1157. pooled = (((a.size - 1) * a.variance) + ((b.size - 1) * b.variance)) / df,
  1158. tstat = (a.mean - b.mean) / sqrt(pooled * (1 / a.size + 1 / b.size)),
  1159. near = abs(1 - a.mean / b.mean) < 0.055 && a.RME < 3 && b.RME < 3;
  1160. // check if the means aren't close and the t-statistic is significant
  1161. return !near && abs(tstat) > getCriticalValue(df) ? (tstat > 0 ? -1 : 1) : 0;
  1162. }
  1163. /**
  1164. * Reset properties and abort if running.
  1165. * @member Benchmark
  1166. * @returns {Object} The benchmark instance.
  1167. */
  1168. function reset() {
  1169. var changed,
  1170. pair,
  1171. me = this,
  1172. source = extend(extend({ }, me.constructor.prototype), me.options),
  1173. pairs = [[source, me]];
  1174. function check(value, key) {
  1175. var other = pair[1][key];
  1176. if (value && isClassOf(value, 'Object')) {
  1177. pairs.push([value, other]);
  1178. }
  1179. else if (!isClassOf(value, 'Function') &&
  1180. key != 'created' && value != other) {
  1181. pair[1][key] = value;
  1182. changed = true;
  1183. }
  1184. }
  1185. if (me.running) {
  1186. // no worries, reset() is called within abort()
  1187. me.abort();
  1188. me.aborted = source.aborted;
  1189. }
  1190. else {
  1191. // check if properties have changed and reset them
  1192. while (pairs.length) {
  1193. each((pair = pairs.pop(), pair[0]), check);
  1194. }
  1195. if (changed) {
  1196. me.emit('reset');
  1197. }
  1198. }
  1199. return me;
  1200. }
  1201. /**
  1202. * Displays relevant benchmark information when coerced to a string.
  1203. * @member Benchmark
  1204. * @returns {String} A string representation of the benchmark instance.
  1205. */
  1206. function toString() {
  1207. var me = this,
  1208. error = me.error,
  1209. hz = me.hz,
  1210. stats = me.stats,
  1211. size = stats.size,
  1212. pm = IN_JAVA ? '+/-' : '\xb1',
  1213. result = me.name || me.id || ('<Test #' + me.fn.uid + '>');
  1214. if (error) {
  1215. result += ': ' + join(error);
  1216. } else {
  1217. result += ' x ' + formatNumber(hz.toFixed(hz < 100 ? 2 : 0)) + ' ops/sec ' + pm +
  1218. stats.RME.toFixed(2) + '% (' + size + ' run' + (size == 1 ? '' : 's') + ' sampled)';
  1219. }
  1220. return result;
  1221. }
  1222. /*--------------------------------------------------------------------------*/
  1223. /**
  1224. * Computes stats on benchmark results.
  1225. * @private
  1226. * @param {Object} me The benchmark instance.
  1227. * @param {Boolean} [async=false] Flag to run asynchronously.
  1228. */
  1229. function compute(me, async) {
  1230. var queue = [],
  1231. sample = [],
  1232. runCount = me.INIT_RUN_COUNT;
  1233. function enqueue(count) {
  1234. while (count--) {
  1235. queue.push(me.clone({
  1236. 'computing': me,
  1237. 'events': { 'start': [update], 'cycle': [update] }
  1238. }));
  1239. }
  1240. }
  1241. function update() {
  1242. // port changes from clone to host
  1243. var clone = this;
  1244. if (me.running) {
  1245. if (clone.cycles) {
  1246. // the me.count and me.cycles props of the host are updated in cycle() below
  1247. me.hz = clone.hz;
  1248. me.times.period = clone.times.period;
  1249. me.INIT_RUN_COUNT = clone.INIT_RUN_COUNT;
  1250. me.emit('cycle');
  1251. }
  1252. else if (clone.error) {
  1253. me.abort();
  1254. me.error = clone.error;
  1255. me.emit('error');
  1256. }
  1257. else {
  1258. // on start
  1259. clone.count = me.INIT_RUN_COUNT;
  1260. }
  1261. } else if (me.aborted) {
  1262. clone.abort();
  1263. }
  1264. }
  1265. function evaluate(clone) {
  1266. var mean,
  1267. moe,
  1268. rme,
  1269. sd,
  1270. sem,
  1271. variance,
  1272. now = +new Date,
  1273. times = me.times,
  1274. aborted = me.aborted,
  1275. elapsed = (now - times.start) / 1e3,
  1276. maxedOut = elapsed > me.MAX_TIME_ELAPSED,
  1277. size = sample.push(clone.times.period),
  1278. varOf = function(sum, x) { return sum + pow(x - mean, 2); };
  1279. // exit early for aborted or unclockable tests
  1280. if (aborted || clone.hz == Infinity) {
  1281. maxedOut = !(size = sample.length = queue.length = 0);
  1282. }
  1283. // simulate onComplete and enqueue additional runs if needed
  1284. if (queue.length < 2) {
  1285. // sample mean (estimate of the population mean)
  1286. mean = getMean(sample);
  1287. // sample variance (estimate of the population variance)
  1288. variance = reduce(sample, varOf, 0) / (size - 1);
  1289. // sample standard deviation (estimate of the population standard deviation)
  1290. sd = sqrt(variance);
  1291. // standard error of the mean (aka the standard deviation of the sampling distribution of the sample mean)
  1292. sem = sd / sqrt(size);
  1293. // margin of error
  1294. moe = sem * getCriticalValue(size - 1);
  1295. // relative margin of error
  1296. rme = (moe / mean) * 100 || 0;
  1297. // if time permits, increase sample size to reduce the margin of error
  1298. if (!maxedOut) {
  1299. enqueue(1);
  1300. }
  1301. else {
  1302. // set host values
  1303. if (!aborted) {
  1304. me.running = false;
  1305. times.stop = now;
  1306. times.elapsed = elapsed;
  1307. extend(me.stats, {
  1308. 'ME': moe,
  1309. 'RME': rme,
  1310. 'SEM': sem,
  1311. 'deviation': sd,
  1312. 'mean': mean,
  1313. 'size': size,
  1314. 'variance': variance
  1315. });
  1316. if (me.hz != Infinity) {
  1317. times.period = mean;
  1318. times.cycle = mean * me.count;
  1319. me.hz = 1 / mean;
  1320. }
  1321. }
  1322. me.INIT_RUN_COUNT = runCount;
  1323. me.emit('complete');
  1324. }
  1325. }
  1326. return !aborted;
  1327. }
  1328. // init sample/queue and begin
  1329. enqueue(me.MIN_SAMPLE_SIZE);
  1330. invoke(queue, { 'name': 'run', 'args': async, 'queued': true, 'onCycle': evaluate });
  1331. }
  1332. /**
  1333. * Runs the benchmark.
  1334. * @member Benchmark
  1335. * @param {Boolean} [async=false] Flag to run asynchronously.
  1336. * @returns {Object} The benchmark instance.
  1337. */
  1338. function run(async) {
  1339. var me = this;
  1340. function cycle() {
  1341. var clocked,
  1342. divisor,
  1343. minTime,
  1344. period,
  1345. count = me.count,
  1346. host = me.computing,
  1347. times = me.times;
  1348. // continue, if not aborted between cycles
  1349. if (me.running) {
  1350. me.cycles++;
  1351. host.cycles++;
  1352. host.count = count;
  1353. try {
  1354. clocked = clock(host);
  1355. minTime = me.MIN_TIME;
  1356. } catch(e) {
  1357. me.abort();
  1358. me.error = e;
  1359. me.emit('error');
  1360. }
  1361. }
  1362. // continue, if not errored
  1363. if (me.running) {
  1364. // time taken to complete last test cycle
  1365. times.cycle = clocked;
  1366. // seconds per operation
  1367. period = times.period = clocked / count;
  1368. // ops per second
  1369. me.hz = 1 / period;
  1370. // do we need to do another cycle?
  1371. me.running = clocked < minTime;
  1372. // avoid working our way up to this next time
  1373. me.INIT_RUN_COUNT = count;
  1374. if (me.running) {
  1375. // tests may clock at 0 when INIT_RUN_COUNT is a small number,
  1376. // to avoid that we set its count to something a bit higher
  1377. if (!clocked && (divisor = CYCLE_DIVISORS[me.cycles]) != null) {
  1378. count = Math.floor(4e6 / divisor);
  1379. }
  1380. // calculate how many more iterations it will take to achive the MIN_TIME
  1381. if (count <= me.count) {
  1382. count += Math.ceil((minTime - clocked) / period);
  1383. }
  1384. me.running = count != Infinity;
  1385. }
  1386. }
  1387. // should we exit early?
  1388. if (me.emit('cycle') === false) {
  1389. me.abort();
  1390. }
  1391. // figure out what to do next
  1392. if (me.running) {
  1393. me.count = count;
  1394. call(me, cycle, async);
  1395. } else {
  1396. // fix TraceMonkey bug
  1397. // http://bugzil.la/509069
  1398. if (window.Benchmark == Benchmark) {
  1399. window.Benchmark = 1;
  1400. window.Benchmark = Benchmark;
  1401. }
  1402. me.emit('complete');
  1403. }
  1404. }
  1405. // set running to false so reset() won't call abort()
  1406. me.running = false;
  1407. me.reset();
  1408. me.running = true;
  1409. me.count = me.INIT_RUN_COUNT;
  1410. me.times.start = +new Date;
  1411. me.emit('start');
  1412. async = (async == null ? me.DEFAULT_ASYNC : async) && HAS_TIMEOUT_API;
  1413. if (me.computing) {
  1414. cycle();
  1415. } else {
  1416. compute(me, async);
  1417. }
  1418. return me;
  1419. }
  1420. /*--------------------------------------------------------------------------*/
  1421. /**
  1422. * Platform object containing browser name, version, and operating system.
  1423. * @static
  1424. * @member Benchmark
  1425. * @type Object
  1426. */
  1427. Benchmark.platform = (function() {
  1428. var me = this,
  1429. alpha = IN_JAVA ? 'a' : '\u03b1',
  1430. beta = IN_JAVA ? 'b' : '\u03b2',
  1431. description = [],
  1432. doc = window.document || {},
  1433. nav = window.navigator || {},
  1434. ua = nav.userAgent || 'unknown platform',
  1435. layout = /Gecko|Trident|WebKit/.exec(ua),
  1436. data = { '6.1': 'Server 2008 R2 / 7', '6.0': 'Server 2008 / Vista', '5.2': 'Server 2003 / XP x64', '5.1': 'XP', '5.0': '2000', '4.0': 'NT', '4.9': 'ME' },
  1437. name = 'Avant Browser,Camino,Epiphany,Fennec,Flock,Galeon,GreenBrowser,iCab,Iron,K-Meleon,Konqueror,Lunascape,Maxthon,Minefield,Nook Browser,RockMelt,SeaMonkey,Sleipnir,SlimBrowser,Sunrise,Swiftfox,Opera Mini,Opera,Chrome,Firefox,IE,Safari',
  1438. os = 'Android,Cygwin,SymbianOS,webOS[ /]\\d,Linux,Mac OS(?: X)?,Macintosh,Windows 98;,Windows ',
  1439. product = 'BlackBerry\\s?\\d+,iP[ao]d,iPhone,Kindle,Nokia,Nook,PlayBook,Samsung,Xoom',
  1440. version = isClassOf(window.opera, 'Opera') && opera.version();
  1441. function format(string) {
  1442. // trim and conditionally capitalize
  1443. return /^(?:webOS|i(?:OS|P))/.test(string = trim(string)) ? string :
  1444. string.charAt(0).toUpperCase() + string.slice(1);
  1445. }
  1446. name = reduce(name.split(','), function(name, guess) {
  1447. return name || RegExp(guess + '\\b', 'i').exec(ua) && guess;
  1448. });
  1449. product = reduce(product.split(','), function(product, guess) {
  1450. if (!product && (product = RegExp(guess + '[^ ();-]*', 'i').exec(ua))) {
  1451. // correct character case and split by forward slash
  1452. if ((product = String(product).replace(RegExp(guess = /\w+/.exec(guess), 'i'), guess).split('/'))[1]) {
  1453. if (/[\d.]+/.test(product[0])) {
  1454. version = version || product[1];
  1455. } else {
  1456. product[0] += ' ' + product[1];
  1457. }
  1458. }
  1459. product = format(product[0].replace(/([a-z])(\d)/i, '$1 $2').split('-')[0]);
  1460. }
  1461. return product;
  1462. });
  1463. os = reduce(os.split(','), function(os, guess) {
  1464. if (!os && (os = RegExp(guess + '[^();/-]*').exec(ua))) {
  1465. // platform tokens defined at
  1466. // http://msdn.microsoft.com/en-us/library/ms537503(VS.85).aspx
  1467. if (/^win/i.test(os) && (data = data[0/*opera fix*/,/[456]\.\d/.exec(os)])) {
  1468. os = 'Windows ' + data;
  1469. }
  1470. // normalize iOS
  1471. else if (/^iP/.test(product)) {
  1472. name || (name = 'Safari');
  1473. os = 'iOS' + ((data = /\bOS ([\d_]+)/.exec(ua)) ? ' ' + data[1] : '');
  1474. }
  1475. // cleanup
  1476. os = String(os).replace(RegExp(guess = /\w+/.exec(guess), 'i'), guess)
  1477. .replace(/\/(\d)/, ' $1').replace(/_/g, '.').replace(/x86\.64/g, 'x86_64')
  1478. .replace('Macintosh', 'Mac OS').replace(/(OS X) Mach$/, '$1').split(' on ')[0];
  1479. }
  1480. return os;
  1481. });
  1482. // detect simulator
  1483. if (/Simulator/i.exec(ua)) {
  1484. product = (product ? product + ' ' : '') + 'Simulator';
  1485. }
  1486. // detect non Firefox Gecko/Safari WebKit based browsers
  1487. if (ua && (data = /^(?:Firefox|Safari|null)/.exec(name))) {
  1488. if (name && !product && /[/,]/.test(ua.slice(ua.indexOf(data + '/') + 8))) {
  1489. name = null;
  1490. }
  1491. if ((data = product || os) && !/^(?:iP|Lin|Mac|Win)/.test(data)) {
  1492. name = /[a-z]+/i.exec(/^And/.test(os) && os || data) + ' Browser';
  1493. }
  1494. }
  1495. // detect non Opera versions
  1496. if (!version) {
  1497. version = reduce(['version', name, 'AdobeAIR', 'Firefox', 'NetFront'], function(version, guess) {
  1498. return version || (RegExp(guess + '(?:-[\\d.]+/|[ /-])([^ ();/-]*)', 'i').exec(ua) || 0)[1] || null;
  1499. });
  1500. }
  1501. // detect server-side js
  1502. if (me && isHostType(me, 'global')) {
  1503. if (typeof exports == 'object' && exports) {
  1504. if (me == window && typeof system == 'object' && (data = system)) {
  1505. name = data.global == global ? 'Narwhal' : 'RingoJS';
  1506. os = data.os || null;
  1507. }
  1508. else if (typeof process == 'object' && (data = process)) {
  1509. name = 'Node.js';
  1510. version = /[\d.]+/.exec(data.version)[0];
  1511. os = data.platform;
  1512. }
  1513. } else if (isClassOf(me.environment, 'Environment')) {
  1514. name = 'Rhino';
  1515. }
  1516. if (IN_JAVA && !os) {
  1517. os = String(java.lang.System.getProperty('os.name'));
  1518. }
  1519. }
  1520. // detect Adobe AIR
  1521. else if (IN_AIR) {
  1522. name = 'Adobe AIR';
  1523. os = runtime.flash.system.Capabilities.os;
  1524. }
  1525. // detect PhantomJS
  1526. else if (isClassOf(data = window.phantom, 'RuntimeObject')) {
  1527. name = 'PhantomJS';
  1528. version = (data = data.version) && (data.major + '.' + data.minor + '.' + data.patch);
  1529. }
  1530. // detect IE compatibility mode
  1531. else if (typeof doc.documentMode == 'number' && (data = /Trident\/(\d+)/.exec(ua))) {
  1532. version = [version, doc.documentMode];
  1533. version[1] = (data = +data[1] + 4) != version[1] ? (layout = null, description.push('running in IE ' + version[1] + ' mode'), data) : version[1];
  1534. version = name == 'IE' ? String(version[1].toFixed(1)) : version[0];
  1535. }
  1536. // detect release phases
  1537. if (version && (data = /(?:[ab]|dp|pre|[ab]\d+pre)(?:\d+\+?)?$/i.exec(version) || /(?:alpha|beta)(?: ?\d)?/i.exec(ua + ';' + nav.appMinorVersion))) {
  1538. version = version.replace(RegExp(data + '\\+?$'), '') + (/^b/i.test(data) ? beta : alpha) + (/\d+\+?/.exec(data) || '');
  1539. }
  1540. // detect Maxthon's unreliable version info
  1541. if (/^Max/.test(name)) {
  1542. version = version && version.replace(/\.[.\d]*/, '.x');
  1543. }
  1544. // detect Firefox nightly
  1545. else if (/^Min/.test(name)) {
  1546. name = 'Firefox';
  1547. version = RegExp(alpha + '|' + beta + '|null').test(version) ? version : version + alpha;
  1548. }
  1549. // detect mobile
  1550. else if (name && (!product || name == 'IE') && !/Bro/.test(name) && /Mobi/.test(ua)) {
  1551. name += ' Mobile';
  1552. }
  1553. // detect unspecified Chrome/Safari versions
  1554. if (data = (/AppleWebKit\/(\d+(?:\.\d+)?)/.exec(ua) || 0)[1]) {
  1555. if (/^And/.exec(os)) {
  1556. data = data < 530 ? 1 : data < 532 ? 2 : data < 532.5 ? 3 : data < 533 ? 4 : data < 534.3 ? 5 : data < 534.7 ? 6 : data < 534.10 ? 7 : data < 534.13 ? 8 : data < 534.16 ? 9 : '10';
  1557. layout = 'like Chrome';
  1558. } else {
  1559. data = data < 400 ? 1 : data < 500 ? 2 : data < 526 ? 3 : data < 533 ? 4 : '4';
  1560. layout = 'like Safari';
  1561. }
  1562. layout += ' ' + (data += typeof data == 'number' ? '.x' : '+');
  1563. version = name == 'Safari' && (!version || parseInt(version) > 45) ? data : version;
  1564. }
  1565. // detect platform preview
  1566. if (RegExp(alpha + '|' + beta).test(version) && typeof external == 'object' && !external) {
  1567. layout = layout && !/like /.test(layout) ? 'rendered by ' + layout : layout;
  1568. description.unshift('platform preview');
  1569. }
  1570. // add layout engine
  1571. if (layout && /Ado|Bro|Lun|Max|Pha|Sle/.test(name)) {
  1572. description.push(layout);
  1573. }
  1574. // combine contextual information
  1575. if (description.length) {
  1576. description = ['(' + description.join('; ') + ')'];
  1577. }
  1578. // append product
  1579. if (product && String(name).indexOf(product) < 0) {
  1580. description.push('on ' + product);
  1581. }
  1582. // add browser/os architecture
  1583. if (/\b(?:WOW|x|IA)64\b/.test(ua)) {
  1584. os = os && os + (/64/.test(os) ? '' : ' x64');
  1585. if (name && (/WOW64/.test(ua) || /\w(?:86|32)$/.test(nav.cpuClass || nav.platform))) {
  1586. description.unshift('x86');
  1587. }
  1588. }
  1589. return {
  1590. 'version': name && version && description.unshift(version) && version,
  1591. 'name': name && description.unshift(name) && name,
  1592. 'os': name && (os = os && format(os)) && description.push(product ? '(' + os + ')' : 'on ' + os) && os,
  1593. 'product': product,
  1594. 'description': description.length ? description.join(' ') : ua,
  1595. 'toString': function() { return this.description; }
  1596. };
  1597. }());
  1598. /*--------------------------------------------------------------------------*/
  1599. extend(Benchmark, {
  1600. /**
  1601. * The version number.
  1602. * @static
  1603. * @member Benchmark
  1604. * @type String
  1605. */
  1606. 'version': '0.2.1',
  1607. /**
  1608. * The default options object copied by instances.
  1609. * @static
  1610. * @member Benchmark
  1611. * @type Object
  1612. */
  1613. 'options': { },
  1614. // generic Array#forEach/for-in
  1615. 'each': each,
  1616. // copy properties to another object
  1617. 'extend': extend,
  1618. // generic Array#filter
  1619. 'filter': filter,
  1620. // converts a number to a comma-separated string
  1621. 'formatNumber': formatNumber,
  1622. // xbrowser Object#hasOwnProperty
  1623. 'hasKey': hasKey,
  1624. // generic Array#indexOf
  1625. 'indexOf': indexOf,
  1626. // invokes a method on each item in an array
  1627. 'invoke': invoke,
  1628. // modifies a string using a template object
  1629. 'interpolate': interpolate,
  1630. // xbrowser Array.isArray
  1631. 'isArray': isArray,
  1632. // checks internal [[Class]] of an object
  1633. 'isClassOf': isClassOf,
  1634. // checks if an object's property is a non-primitive value
  1635. 'isHostType': isHostType,
  1636. // generic Array#join for arrays and objects
  1637. 'join': join,
  1638. // generic Array#map
  1639. 'map': map,
  1640. // no operation
  1641. 'noop': noop,
  1642. // retrieves a property value from each item in an array
  1643. 'pluck': pluck,
  1644. // generic Array#reduce
  1645. 'reduce': reduce,
  1646. // generic String#trim
  1647. 'trim': trim
  1648. });
  1649. /*--------------------------------------------------------------------------*/
  1650. // IE may ignore `toString` in a for-in loop
  1651. Benchmark.prototype.toString = toString;
  1652. extend(Benchmark.prototype, {
  1653. /**
  1654. * The delay between test cycles (secs).
  1655. * @member Benchmark
  1656. * @type Number
  1657. */
  1658. 'CYCLE_DELAY': 0.005,
  1659. /**
  1660. * A flag to indicate methods will run asynchronously by default.
  1661. * @member Benchmark
  1662. * @type Boolean
  1663. */
  1664. 'DEFAULT_ASYNC': false,
  1665. /**
  1666. * The default number of times to execute a test on a benchmark's first cycle.
  1667. * @member Benchmark
  1668. * @type Number
  1669. */
  1670. 'INIT_RUN_COUNT': 5,
  1671. /**
  1672. * The maximum time a benchmark is allowed to run before finishing (secs).
  1673. * @member Benchmark
  1674. * @type Number
  1675. */
  1676. 'MAX_TIME_ELAPSED': 5,
  1677. /**
  1678. * The minimum sample size required to perform statistical analysis.
  1679. * @member Benchmark
  1680. * @type Number
  1681. */
  1682. 'MIN_SAMPLE_SIZE': 5,
  1683. /**
  1684. * The time needed to reduce the percent uncertainty of measurement to 1% (secs).
  1685. * @member Benchmark
  1686. * @type Number
  1687. */
  1688. 'MIN_TIME': 0,
  1689. /**
  1690. * The number of times a test was executed.
  1691. * @member Benchmark
  1692. * @type Number
  1693. */
  1694. 'count': 0,
  1695. /**
  1696. * A timestamp of when the benchmark was created.
  1697. * @member Benchmark
  1698. * @type Number
  1699. */
  1700. 'created': 0,
  1701. /**
  1702. * The number of cycles performed while benchmarking.
  1703. * @member Benchmark
  1704. * @type Number
  1705. */
  1706. 'cycles': 0,
  1707. /**
  1708. * The error object if the test failed.
  1709. * @member Benchmark
  1710. * @type Object|Null
  1711. */
  1712. 'error': null,
  1713. /**
  1714. * The number of executions per second.
  1715. * @member Benchmark
  1716. * @type Number
  1717. */
  1718. 'hz': 0,
  1719. /**
  1720. * A flag to indicate if the benchmark is aborted.
  1721. * @member Benchmark
  1722. * @type Boolean
  1723. */
  1724. 'aborted': false,
  1725. /**
  1726. * A flag to indicate if the benchmark is running.
  1727. * @member Benchmark
  1728. * @type Boolean
  1729. */
  1730. 'running': false,
  1731. /**
  1732. * Alias of [`Benchmark#addListener`](#Benchmark:addListener).
  1733. * @member Benchmark, Benchmark.Suite
  1734. */
  1735. 'on': addListener,
  1736. /**
  1737. * Compiled into the test and executed immediately **before** the test loop.
  1738. * @member Benchmark
  1739. * @type Function
  1740. * @example
  1741. *
  1742. * var bench = new Benchmark({
  1743. * 'fn': function() {
  1744. * a += 1;
  1745. * },
  1746. * 'setup': function() {
  1747. * // reset local var `a` at the beginning of each test cycle
  1748. * a = 0;
  1749. * }
  1750. * });
  1751. *
  1752. * // compiles into something like:
  1753. * var a = 0;
  1754. * var start = new Date;
  1755. * while (count--) {
  1756. * a += 1;
  1757. * }
  1758. * var end = new Date - start;
  1759. */
  1760. 'setup': noop,
  1761. /**
  1762. * Compiled into the test and executed immediately **after** the test loop.
  1763. * @member Benchmark
  1764. * @type Function
  1765. */
  1766. 'teardown': noop,
  1767. /**
  1768. * An object of stats including mean, margin or error, and standard deviation.
  1769. * @member Benchmark
  1770. * @type Object
  1771. */
  1772. 'stats': {
  1773. /**
  1774. * The margin of error.
  1775. * @member Benchmark#stats
  1776. * @type Number
  1777. */
  1778. 'ME': 0,
  1779. /**
  1780. * The relative margin of error (expressed as a percentage of the mean).
  1781. * @member Benchmark#stats
  1782. * @type Number
  1783. */
  1784. 'RME': 0,
  1785. /**
  1786. * The standard error of the mean.
  1787. * @member Benchmark#stats
  1788. * @type Number
  1789. */
  1790. 'SEM': 0,
  1791. /**
  1792. * The sample standard deviation.
  1793. * @member Benchmark#stats
  1794. * @type Number
  1795. */
  1796. 'deviation': 0,
  1797. /**
  1798. * The sample arithmetic mean.
  1799. * @member Benchmark#stats
  1800. * @type Number
  1801. */
  1802. 'mean': 0,
  1803. /**
  1804. * The sample size.
  1805. * @member Benchmark#stats
  1806. * @type Number
  1807. */
  1808. 'size': 0,
  1809. /**
  1810. * The sample variance.
  1811. * @member Benchmark#stats
  1812. * @type Number
  1813. */
  1814. 'variance': 0
  1815. },
  1816. /**
  1817. * An object of timing data including cycle, elapsed, period, start, and stop.
  1818. * @member Benchmark
  1819. * @type Object
  1820. */
  1821. 'times': {
  1822. /**
  1823. * The time taken to complete the last cycle (secs)
  1824. * @member Benchmark#times
  1825. * @type Number
  1826. */
  1827. 'cycle': 0,
  1828. /**
  1829. * The time taken to complete the benchmark (secs).
  1830. * @member Benchmark#times
  1831. * @type Number
  1832. */
  1833. 'elapsed': 0,
  1834. /**
  1835. * The time taken to execute the test once (secs).
  1836. * @member Benchmark#times
  1837. * @type Number
  1838. */
  1839. 'period': 0,
  1840. /**
  1841. * A timestamp of when the benchmark started (ms).
  1842. * @member Benchmark#times
  1843. * @type Number
  1844. */
  1845. 'start': 0,
  1846. /**
  1847. * A timestamp of when the benchmark finished (ms).
  1848. * @member Benchmark#times
  1849. * @type Number
  1850. */
  1851. 'stop': 0
  1852. },
  1853. // aborts benchmark (does not record times)
  1854. 'abort': abort,
  1855. // registers a single listener
  1856. 'addListener': addListener,
  1857. // creates a new benchmark using the same test and options
  1858. 'clone': clone,
  1859. // compares benchmark's hertz with another
  1860. 'compare': compare,
  1861. // executes listeners of a specified type
  1862. 'emit': emit,
  1863. // removes all listeners of a specified type
  1864. 'removeAllListeners': removeAllListeners,
  1865. // removes a single listener
  1866. 'removeListener': removeListener,
  1867. // reset benchmark properties
  1868. 'reset': reset,
  1869. // runs the benchmark
  1870. 'run': run
  1871. });
  1872. /*--------------------------------------------------------------------------*/
  1873. /**
  1874. * The default options object copied by instances.
  1875. * @static
  1876. * @member Benchmark.Suite
  1877. * @type Object
  1878. */
  1879. Suite.options = { };
  1880. /*--------------------------------------------------------------------------*/
  1881. extend(Suite.prototype, {
  1882. /**
  1883. * The number of benchmarks in the suite.
  1884. * @member Benchmark.Suite
  1885. * @type Number
  1886. */
  1887. 'length': 0,
  1888. /**
  1889. * A flag to indicate if the suite is aborted.
  1890. * @member Benchmark.Suite
  1891. * @type Boolean
  1892. */
  1893. 'aborted': false,
  1894. /**
  1895. * A flag to indicate if the suite is running.
  1896. * @member Benchmark.Suite
  1897. * @type Boolean
  1898. */
  1899. 'running': false,
  1900. /**
  1901. * A bare-bones `Array#forEach` solution.
  1902. * Callbacks may terminate the loop by explicitly returning `false`.
  1903. * @member Benchmark.Suite
  1904. * @param {Function} callback The function called per iteration.
  1905. * @returns {Object} The suite iterated over.
  1906. */
  1907. 'each': methodize(each),
  1908. /**
  1909. * A bare-bones `Array#indexOf` solution.
  1910. * @member Benchmark.Suite
  1911. * @param {Mixed} value The value to search for.
  1912. * @returns {Number} The index of the matched value or `-1`.
  1913. */
  1914. 'indexOf': methodize(indexOf),
  1915. /**
  1916. * Invokes a method on all benchmarks in the suite.
  1917. * @member Benchmark.Suite
  1918. * @param {String|Object} name The name of the method to invoke OR options object.
  1919. * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with.
  1920. * @returns {Array} A new array of values returned from each method invoked.
  1921. */
  1922. 'invoke': methodize(invoke),
  1923. /**
  1924. * A bare-bones `Array#map` solution.
  1925. * @member Benchmark.Suite
  1926. * @param {Function} callback The function called per iteration.
  1927. * @returns {Array} A new array of values returned by the callback.
  1928. */
  1929. 'map': methodize(map),
  1930. /**
  1931. * Retrieves the value of a specified property from all benchmarks in the suite.
  1932. * @member Benchmark.Suite
  1933. * @param {String} property The property to pluck.
  1934. * @returns {Array} A new array of property values.
  1935. */
  1936. 'pluck': methodize(pluck),
  1937. /**
  1938. * A bare-bones `Array#reduce` solution.
  1939. * @member Benchmark.Suite
  1940. * @param {Function} callback The function called per iteration.
  1941. * @param {Mixed} accumulator Initial value of the accumulator.
  1942. * @returns {Mixed} The accumulator.
  1943. */
  1944. 'reduce': methodize(reduce),
  1945. // aborts all benchmarks in the suite
  1946. 'abort': abortSuite,
  1947. // adds a benchmark to the suite
  1948. 'add': add,
  1949. // registers a single listener
  1950. 'addListener': addListener,
  1951. // creates a new suite with cloned benchmarks
  1952. 'clone': cloneSuite,
  1953. // executes listeners of a specified type
  1954. 'emit': emit,
  1955. // creates a new suite of filtered benchmarks
  1956. 'filter': filterSuite,
  1957. // alias of addListener
  1958. 'on': addListener,
  1959. // removes all listeners of a specified type
  1960. 'removeAllListeners': removeAllListeners,
  1961. // removes a single listener
  1962. 'removeListener': removeListener,
  1963. // resets all benchmarks in the suite
  1964. 'reset': resetSuite,
  1965. // runs all benchmarks in the suite
  1966. 'run': runSuite,
  1967. // array methods
  1968. 'concat': [].concat,
  1969. 'join': [].join,
  1970. 'pop': aloClean([].pop),
  1971. 'push': [].push,
  1972. 'reverse': [].reverse,
  1973. 'shift': shift,
  1974. 'slice': slice,
  1975. 'sort': [].sort,
  1976. 'splice': aloClean([].splice),
  1977. 'unshift': [].unshift
  1978. });
  1979. /*--------------------------------------------------------------------------*/
  1980. // expose Suite
  1981. Benchmark.Suite = Suite;
  1982. // expose Benchmark
  1983. if (typeof exports == 'object' && exports && typeof global == 'object' && global) {
  1984. window = global;
  1985. if (typeof module == 'object' && module && module.exports == exports) {
  1986. module.exports = Benchmark;
  1987. } else {
  1988. exports.Benchmark = Benchmark;
  1989. }
  1990. } else {
  1991. window.Benchmark = Benchmark;
  1992. }
  1993. // trigger clock's lazy define early to avoid a security error
  1994. if (IN_AIR) {
  1995. clock({ 'fn': noop, 'count': 1 });
  1996. }
  1997. // feature detect
  1998. HAS_TIMEOUT_API = isHostType(window, 'setTimeout') &&
  1999. isHostType(window, 'clearTimeout');
  2000. }(this));