OLD | NEW |
(Empty) | |
| 1 /*! |
| 2 * Benchmark.js v1.0.0 <http://benchmarkjs.com/> |
| 3 * Copyright 2010-2012 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, undefined) { |
| 9 'use strict'; |
| 10 |
| 11 /** Used to assign each benchmark an incrimented id */ |
| 12 var counter = 0; |
| 13 |
| 14 /** Detect DOM document object */ |
| 15 var doc = isHostType(window, 'document') && document; |
| 16 |
| 17 /** Detect free variable `define` */ |
| 18 var freeDefine = typeof define == 'function' && |
| 19 typeof define.amd == 'object' && define.amd && define; |
| 20 |
| 21 /** Detect free variable `exports` */ |
| 22 var freeExports = typeof exports == 'object' && exports && |
| 23 (typeof global == 'object' && global && global == global.global && (window =
global), exports); |
| 24 |
| 25 /** Detect free variable `require` */ |
| 26 var freeRequire = typeof require == 'function' && require; |
| 27 |
| 28 /** Used to crawl all properties regardless of enumerability */ |
| 29 var getAllKeys = Object.getOwnPropertyNames; |
| 30 |
| 31 /** Used to get property descriptors */ |
| 32 var getDescriptor = Object.getOwnPropertyDescriptor; |
| 33 |
| 34 /** Used in case an object doesn't have its own method */ |
| 35 var hasOwnProperty = {}.hasOwnProperty; |
| 36 |
| 37 /** Used to check if an object is extensible */ |
| 38 var isExtensible = Object.isExtensible || function() { return true; }; |
| 39 |
| 40 /** Used to access Wade Simmons' Node microtime module */ |
| 41 var microtimeObject = req('microtime'); |
| 42 |
| 43 /** Used to access the browser's high resolution timer */ |
| 44 var perfObject = isHostType(window, 'performance') && performance; |
| 45 |
| 46 /** Used to call the browser's high resolution timer */ |
| 47 var perfName = perfObject && ( |
| 48 perfObject.now && 'now' || |
| 49 perfObject.webkitNow && 'webkitNow' |
| 50 ); |
| 51 |
| 52 /** Used to access Node's high resolution timer */ |
| 53 var processObject = isHostType(window, 'process') && process; |
| 54 |
| 55 /** Used to check if an own property is enumerable */ |
| 56 var propertyIsEnumerable = {}.propertyIsEnumerable; |
| 57 |
| 58 /** Used to set property descriptors */ |
| 59 var setDescriptor = Object.defineProperty; |
| 60 |
| 61 /** Used to resolve a value's internal [[Class]] */ |
| 62 var toString = {}.toString; |
| 63 |
| 64 /** Used to prevent a `removeChild` memory leak in IE < 9 */ |
| 65 var trash = doc && doc.createElement('div'); |
| 66 |
| 67 /** Used to integrity check compiled tests */ |
| 68 var uid = 'uid' + (+new Date); |
| 69 |
| 70 /** Used to avoid infinite recursion when methods call each other */ |
| 71 var calledBy = {}; |
| 72 |
| 73 /** Used to avoid hz of Infinity */ |
| 74 var divisors = { |
| 75 '1': 4096, |
| 76 '2': 512, |
| 77 '3': 64, |
| 78 '4': 8, |
| 79 '5': 0 |
| 80 }; |
| 81 |
| 82 /** |
| 83 * T-Distribution two-tailed critical values for 95% confidence |
| 84 * http://www.itl.nist.gov/div898/handbook/eda/section3/eda3672.htm |
| 85 */ |
| 86 var tTable = { |
| 87 '1': 12.706,'2': 4.303, '3': 3.182, '4': 2.776, '5': 2.571, '6': 2.447
, |
| 88 '7': 2.365, '8': 2.306, '9': 2.262, '10': 2.228, '11': 2.201, '12': 2.179
, |
| 89 '13': 2.16, '14': 2.145, '15': 2.131, '16': 2.12, '17': 2.11, '18': 2.101
, |
| 90 '19': 2.093, '20': 2.086, '21': 2.08, '22': 2.074, '23': 2.069, '24': 2.064
, |
| 91 '25': 2.06, '26': 2.056, '27': 2.052, '28': 2.048, '29': 2.045, '30': 2.042
, |
| 92 'infinity': 1.96 |
| 93 }; |
| 94 |
| 95 /** |
| 96 * Critical Mann-Whitney U-values for 95% confidence |
| 97 * http://www.saburchill.com/IBbiology/stats/003.html |
| 98 */ |
| 99 var uTable = { |
| 100 '5': [0, 1, 2], |
| 101 '6': [1, 2, 3, 5], |
| 102 '7': [1, 3, 5, 6, 8], |
| 103 '8': [2, 4, 6, 8, 10, 13], |
| 104 '9': [2, 4, 7, 10, 12, 15, 17], |
| 105 '10': [3, 5, 8, 11, 14, 17, 20, 23], |
| 106 '11': [3, 6, 9, 13, 16, 19, 23, 26, 30], |
| 107 '12': [4, 7, 11, 14, 18, 22, 26, 29, 33, 37], |
| 108 '13': [4, 8, 12, 16, 20, 24, 28, 33, 37, 41, 45], |
| 109 '14': [5, 9, 13, 17, 22, 26, 31, 36, 40, 45, 50, 55], |
| 110 '15': [5, 10, 14, 19, 24, 29, 34, 39, 44, 49, 54, 59, 64], |
| 111 '16': [6, 11, 15, 21, 26, 31, 37, 42, 47, 53, 59, 64, 70, 75], |
| 112 '17': [6, 11, 17, 22, 28, 34, 39, 45, 51, 57, 63, 67, 75, 81, 87], |
| 113 '18': [7, 12, 18, 24, 30, 36, 42, 48, 55, 61, 67, 74, 80, 86, 93, 99], |
| 114 '19': [7, 13, 19, 25, 32, 38, 45, 52, 58, 65, 72, 78, 85, 92, 99, 106, 113], |
| 115 '20': [8, 14, 20, 27, 34, 41, 48, 55, 62, 69, 76, 83, 90, 98, 105, 112, 119,
127], |
| 116 '21': [8, 15, 22, 29, 36, 43, 50, 58, 65, 73, 80, 88, 96, 103, 111, 119, 126
, 134, 142], |
| 117 '22': [9, 16, 23, 30, 38, 45, 53, 61, 69, 77, 85, 93, 101, 109, 117, 125, 13
3, 141, 150, 158], |
| 118 '23': [9, 17, 24, 32, 40, 48, 56, 64, 73, 81, 89, 98, 106, 115, 123, 132, 14
0, 149, 157, 166, 175], |
| 119 '24': [10, 17, 25, 33, 42, 50, 59, 67, 76, 85, 94, 102, 111, 120, 129, 138,
147, 156, 165, 174, 183, 192], |
| 120 '25': [10, 18, 27, 35, 44, 53, 62, 71, 80, 89, 98, 107, 117, 126, 135, 145,
154, 163, 173, 182, 192, 201, 211], |
| 121 '26': [11, 19, 28, 37, 46, 55, 64, 74, 83, 93, 102, 112, 122, 132, 141, 151,
161, 171, 181, 191, 200, 210, 220, 230], |
| 122 '27': [11, 20, 29, 38, 48, 57, 67, 77, 87, 97, 107, 118, 125, 138, 147, 158,
168, 178, 188, 199, 209, 219, 230, 240, 250], |
| 123 '28': [12, 21, 30, 40, 50, 60, 70, 80, 90, 101, 111, 122, 132, 143, 154, 164
, 175, 186, 196, 207, 218, 228, 239, 250, 261, 272], |
| 124 '29': [13, 22, 32, 42, 52, 62, 73, 83, 94, 105, 116, 127, 138, 149, 160, 171
, 182, 193, 204, 215, 226, 238, 249, 260, 271, 282, 294], |
| 125 '30': [13, 23, 33, 43, 54, 65, 76, 87, 98, 109, 120, 131, 143, 154, 166, 177
, 189, 200, 212, 223, 235, 247, 258, 270, 282, 293, 305, 317] |
| 126 }; |
| 127 |
| 128 /** |
| 129 * An object used to flag environments/features. |
| 130 * |
| 131 * @static |
| 132 * @memberOf Benchmark |
| 133 * @type Object |
| 134 */ |
| 135 var support = {}; |
| 136 |
| 137 (function() { |
| 138 |
| 139 /** |
| 140 * Detect Adobe AIR. |
| 141 * |
| 142 * @memberOf Benchmark.support |
| 143 * @type Boolean |
| 144 */ |
| 145 support.air = isClassOf(window.runtime, 'ScriptBridgingProxyObject'); |
| 146 |
| 147 /** |
| 148 * Detect if `arguments` objects have the correct internal [[Class]] value. |
| 149 * |
| 150 * @memberOf Benchmark.support |
| 151 * @type Boolean |
| 152 */ |
| 153 support.argumentsClass = isClassOf(arguments, 'Arguments'); |
| 154 |
| 155 /** |
| 156 * Detect if in a browser environment. |
| 157 * |
| 158 * @memberOf Benchmark.support |
| 159 * @type Boolean |
| 160 */ |
| 161 support.browser = doc && isHostType(window, 'navigator'); |
| 162 |
| 163 /** |
| 164 * Detect if strings support accessing characters by index. |
| 165 * |
| 166 * @memberOf Benchmark.support |
| 167 * @type Boolean |
| 168 */ |
| 169 support.charByIndex = |
| 170 // IE 8 supports indexes on string literals but not string objects |
| 171 ('x'[0] + Object('x')[0]) == 'xx'; |
| 172 |
| 173 /** |
| 174 * Detect if strings have indexes as own properties. |
| 175 * |
| 176 * @memberOf Benchmark.support |
| 177 * @type Boolean |
| 178 */ |
| 179 support.charByOwnIndex = |
| 180 // Narwhal, Rhino, RingoJS, IE 8, and Opera < 10.52 support indexes on |
| 181 // strings but don't detect them as own properties |
| 182 support.charByIndex && hasKey('x', '0'); |
| 183 |
| 184 /** |
| 185 * Detect if Java is enabled/exposed. |
| 186 * |
| 187 * @memberOf Benchmark.support |
| 188 * @type Boolean |
| 189 */ |
| 190 support.java = isClassOf(window.java, 'JavaPackage'); |
| 191 |
| 192 /** |
| 193 * Detect if the Timers API exists. |
| 194 * |
| 195 * @memberOf Benchmark.support |
| 196 * @type Boolean |
| 197 */ |
| 198 support.timeout = isHostType(window, 'setTimeout') && isHostType(window, 'cl
earTimeout'); |
| 199 |
| 200 /** |
| 201 * Detect if functions support decompilation. |
| 202 * |
| 203 * @name decompilation |
| 204 * @memberOf Benchmark.support |
| 205 * @type Boolean |
| 206 */ |
| 207 try { |
| 208 // Safari 2.x removes commas in object literals |
| 209 // from Function#toString results |
| 210 // http://webk.it/11609 |
| 211 // Firefox 3.6 and Opera 9.25 strip grouping |
| 212 // parentheses from Function#toString results |
| 213 // http://bugzil.la/559438 |
| 214 support.decompilation = Function( |
| 215 'return (' + (function(x) { return { 'x': '' + (1 + x) + '', 'y': 0 }; }
) + ')' |
| 216 )()(0).x === '1'; |
| 217 } catch(e) { |
| 218 support.decompilation = false; |
| 219 } |
| 220 |
| 221 /** |
| 222 * Detect ES5+ property descriptor API. |
| 223 * |
| 224 * @name descriptors |
| 225 * @memberOf Benchmark.support |
| 226 * @type Boolean |
| 227 */ |
| 228 try { |
| 229 var o = {}; |
| 230 support.descriptors = (setDescriptor(o, o, o), 'value' in getDescriptor(o,
o)); |
| 231 } catch(e) { |
| 232 support.descriptors = false; |
| 233 } |
| 234 |
| 235 /** |
| 236 * Detect ES5+ Object.getOwnPropertyNames(). |
| 237 * |
| 238 * @name getAllKeys |
| 239 * @memberOf Benchmark.support |
| 240 * @type Boolean |
| 241 */ |
| 242 try { |
| 243 support.getAllKeys = /\bvalueOf\b/.test(getAllKeys(Object.prototype)); |
| 244 } catch(e) { |
| 245 support.getAllKeys = false; |
| 246 } |
| 247 |
| 248 /** |
| 249 * Detect if own properties are iterated before inherited properties (all bu
t IE < 9). |
| 250 * |
| 251 * @name iteratesOwnLast |
| 252 * @memberOf Benchmark.support |
| 253 * @type Boolean |
| 254 */ |
| 255 support.iteratesOwnFirst = (function() { |
| 256 var props = []; |
| 257 function ctor() { this.x = 1; } |
| 258 ctor.prototype = { 'y': 1 }; |
| 259 for (var prop in new ctor) { props.push(prop); } |
| 260 return props[0] == 'x'; |
| 261 }()); |
| 262 |
| 263 /** |
| 264 * Detect if a node's [[Class]] is resolvable (all but IE < 9) |
| 265 * and that the JS engine errors when attempting to coerce an object to a |
| 266 * string without a `toString` property value of `typeof` "function". |
| 267 * |
| 268 * @name nodeClass |
| 269 * @memberOf Benchmark.support |
| 270 * @type Boolean |
| 271 */ |
| 272 try { |
| 273 support.nodeClass = ({ 'toString': 0 } + '', toString.call(doc || 0) != '[
object Object]'); |
| 274 } catch(e) { |
| 275 support.nodeClass = true; |
| 276 } |
| 277 }()); |
| 278 |
| 279 /** |
| 280 * Timer object used by `clock()` and `Deferred#resolve`. |
| 281 * |
| 282 * @private |
| 283 * @type Object |
| 284 */ |
| 285 var timer = { |
| 286 |
| 287 /** |
| 288 * The timer namespace object or constructor. |
| 289 * |
| 290 * @private |
| 291 * @memberOf timer |
| 292 * @type Function|Object |
| 293 */ |
| 294 'ns': Date, |
| 295 |
| 296 /** |
| 297 * Starts the deferred timer. |
| 298 * |
| 299 * @private |
| 300 * @memberOf timer |
| 301 * @param {Object} deferred The deferred instance. |
| 302 */ |
| 303 'start': null, // lazy defined in `clock()` |
| 304 |
| 305 /** |
| 306 * Stops the deferred timer. |
| 307 * |
| 308 * @private |
| 309 * @memberOf timer |
| 310 * @param {Object} deferred The deferred instance. |
| 311 */ |
| 312 'stop': null // lazy defined in `clock()` |
| 313 }; |
| 314 |
| 315 /** Shortcut for inverse results */ |
| 316 var noArgumentsClass = !support.argumentsClass, |
| 317 noCharByIndex = !support.charByIndex, |
| 318 noCharByOwnIndex = !support.charByOwnIndex; |
| 319 |
| 320 /** Math shortcuts */ |
| 321 var abs = Math.abs, |
| 322 floor = Math.floor, |
| 323 max = Math.max, |
| 324 min = Math.min, |
| 325 pow = Math.pow, |
| 326 sqrt = Math.sqrt; |
| 327 |
| 328 /*--------------------------------------------------------------------------*/ |
| 329 |
| 330 /** |
| 331 * The Benchmark constructor. |
| 332 * |
| 333 * @constructor |
| 334 * @param {String} name A name to identify the benchmark. |
| 335 * @param {Function|String} fn The test to benchmark. |
| 336 * @param {Object} [options={}] Options object. |
| 337 * @example |
| 338 * |
| 339 * // basic usage (the `new` operator is optional) |
| 340 * var bench = new Benchmark(fn); |
| 341 * |
| 342 * // or using a name first |
| 343 * var bench = new Benchmark('foo', fn); |
| 344 * |
| 345 * // or with options |
| 346 * var bench = new Benchmark('foo', fn, { |
| 347 * |
| 348 * // displayed by Benchmark#toString if `name` is not available |
| 349 * 'id': 'xyz', |
| 350 * |
| 351 * // called when the benchmark starts running |
| 352 * 'onStart': onStart, |
| 353 * |
| 354 * // called after each run cycle |
| 355 * 'onCycle': onCycle, |
| 356 * |
| 357 * // called when aborted |
| 358 * 'onAbort': onAbort, |
| 359 * |
| 360 * // called when a test errors |
| 361 * 'onError': onError, |
| 362 * |
| 363 * // called when reset |
| 364 * 'onReset': onReset, |
| 365 * |
| 366 * // called when the benchmark completes running |
| 367 * 'onComplete': onComplete, |
| 368 * |
| 369 * // compiled/called before the test loop |
| 370 * 'setup': setup, |
| 371 * |
| 372 * // compiled/called after the test loop |
| 373 * 'teardown': teardown |
| 374 * }); |
| 375 * |
| 376 * // or name and options |
| 377 * var bench = new Benchmark('foo', { |
| 378 * |
| 379 * // a flag to indicate the benchmark is deferred |
| 380 * 'defer': true, |
| 381 * |
| 382 * // benchmark test function |
| 383 * 'fn': function(deferred) { |
| 384 * // call resolve() when the deferred test is finished |
| 385 * deferred.resolve(); |
| 386 * } |
| 387 * }); |
| 388 * |
| 389 * // or options only |
| 390 * var bench = new Benchmark({ |
| 391 * |
| 392 * // benchmark name |
| 393 * 'name': 'foo', |
| 394 * |
| 395 * // benchmark test as a string |
| 396 * 'fn': '[1,2,3,4].sort()' |
| 397 * }); |
| 398 * |
| 399 * // a test's `this` binding is set to the benchmark instance |
| 400 * var bench = new Benchmark('foo', function() { |
| 401 * 'My name is '.concat(this.name); // My name is foo |
| 402 * }); |
| 403 */ |
| 404 function Benchmark(name, fn, options) { |
| 405 var me = this; |
| 406 |
| 407 // allow instance creation without the `new` operator |
| 408 if (me == null || me.constructor != Benchmark) { |
| 409 return new Benchmark(name, fn, options); |
| 410 } |
| 411 // juggle arguments |
| 412 if (isClassOf(name, 'Object')) { |
| 413 // 1 argument (options) |
| 414 options = name; |
| 415 } |
| 416 else if (isClassOf(name, 'Function')) { |
| 417 // 2 arguments (fn, options) |
| 418 options = fn; |
| 419 fn = name; |
| 420 } |
| 421 else if (isClassOf(fn, 'Object')) { |
| 422 // 2 arguments (name, options) |
| 423 options = fn; |
| 424 fn = null; |
| 425 me.name = name; |
| 426 } |
| 427 else { |
| 428 // 3 arguments (name, fn [, options]) |
| 429 me.name = name; |
| 430 } |
| 431 setOptions(me, options); |
| 432 me.id || (me.id = ++counter); |
| 433 me.fn == null && (me.fn = fn); |
| 434 me.stats = deepClone(me.stats); |
| 435 me.times = deepClone(me.times); |
| 436 } |
| 437 |
| 438 /** |
| 439 * The Deferred constructor. |
| 440 * |
| 441 * @constructor |
| 442 * @memberOf Benchmark |
| 443 * @param {Object} clone The cloned benchmark instance. |
| 444 */ |
| 445 function Deferred(clone) { |
| 446 var me = this; |
| 447 if (me == null || me.constructor != Deferred) { |
| 448 return new Deferred(clone); |
| 449 } |
| 450 me.benchmark = clone; |
| 451 clock(me); |
| 452 } |
| 453 |
| 454 /** |
| 455 * The Event constructor. |
| 456 * |
| 457 * @constructor |
| 458 * @memberOf Benchmark |
| 459 * @param {String|Object} type The event type. |
| 460 */ |
| 461 function Event(type) { |
| 462 var me = this; |
| 463 return (me == null || me.constructor != Event) |
| 464 ? new Event(type) |
| 465 : (type instanceof Event) |
| 466 ? type |
| 467 : extend(me, { 'timeStamp': +new Date }, typeof type == 'string' ? { '
type': type } : type); |
| 468 } |
| 469 |
| 470 /** |
| 471 * The Suite constructor. |
| 472 * |
| 473 * @constructor |
| 474 * @memberOf Benchmark |
| 475 * @param {String} name A name to identify the suite. |
| 476 * @param {Object} [options={}] Options object. |
| 477 * @example |
| 478 * |
| 479 * // basic usage (the `new` operator is optional) |
| 480 * var suite = new Benchmark.Suite; |
| 481 * |
| 482 * // or using a name first |
| 483 * var suite = new Benchmark.Suite('foo'); |
| 484 * |
| 485 * // or with options |
| 486 * var suite = new Benchmark.Suite('foo', { |
| 487 * |
| 488 * // called when the suite starts running |
| 489 * 'onStart': onStart, |
| 490 * |
| 491 * // called between running benchmarks |
| 492 * 'onCycle': onCycle, |
| 493 * |
| 494 * // called when aborted |
| 495 * 'onAbort': onAbort, |
| 496 * |
| 497 * // called when a test errors |
| 498 * 'onError': onError, |
| 499 * |
| 500 * // called when reset |
| 501 * 'onReset': onReset, |
| 502 * |
| 503 * // called when the suite completes running |
| 504 * 'onComplete': onComplete |
| 505 * }); |
| 506 */ |
| 507 function Suite(name, options) { |
| 508 var me = this; |
| 509 |
| 510 // allow instance creation without the `new` operator |
| 511 if (me == null || me.constructor != Suite) { |
| 512 return new Suite(name, options); |
| 513 } |
| 514 // juggle arguments |
| 515 if (isClassOf(name, 'Object')) { |
| 516 // 1 argument (options) |
| 517 options = name; |
| 518 } else { |
| 519 // 2 arguments (name [, options]) |
| 520 me.name = name; |
| 521 } |
| 522 setOptions(me, options); |
| 523 } |
| 524 |
| 525 /*--------------------------------------------------------------------------*/ |
| 526 |
| 527 /** |
| 528 * Note: Some array methods have been implemented in plain JavaScript to avoid |
| 529 * bugs in IE, Opera, Rhino, and Mobile Safari. |
| 530 * |
| 531 * IE compatibility mode and IE < 9 have buggy Array `shift()` and `splice()` |
| 532 * functions that fail to remove the last element, `object[0]`, of |
| 533 * array-like-objects even though the `length` property is set to `0`. |
| 534 * The `shift()` method is buggy in IE 8 compatibility mode, while `splice()` |
| 535 * is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE
9. |
| 536 * |
| 537 * In Opera < 9.50 and some older/beta Mobile Safari versions using `unshift()
` |
| 538 * generically to augment the `arguments` object will pave the value at index
0 |
| 539 * without incrimenting the other values's indexes. |
| 540 * https://github.com/documentcloud/underscore/issues/9 |
| 541 * |
| 542 * Rhino and environments it powers, like Narwhal and RingoJS, may have |
| 543 * buggy Array `concat()`, `reverse()`, `shift()`, `slice()`, `splice()` and |
| 544 * `unshift()` functions that make sparse arrays non-sparse by assigning the |
| 545 * undefined indexes a value of undefined. |
| 546 * https://github.com/mozilla/rhino/commit/702abfed3f8ca043b2636efd31c14ba7552
603dd |
| 547 */ |
| 548 |
| 549 /** |
| 550 * Creates an array containing the elements of the host array followed by the |
| 551 * elements of each argument in order. |
| 552 * |
| 553 * @memberOf Benchmark.Suite |
| 554 * @returns {Array} The new array. |
| 555 */ |
| 556 function concat() { |
| 557 var value, |
| 558 j = -1, |
| 559 length = arguments.length, |
| 560 result = slice.call(this), |
| 561 index = result.length; |
| 562 |
| 563 while (++j < length) { |
| 564 value = arguments[j]; |
| 565 if (isClassOf(value, 'Array')) { |
| 566 for (var k = 0, l = value.length; k < l; k++, index++) { |
| 567 if (k in value) { |
| 568 result[index] = value[k]; |
| 569 } |
| 570 } |
| 571 } else { |
| 572 result[index++] = value; |
| 573 } |
| 574 } |
| 575 return result; |
| 576 } |
| 577 |
| 578 /** |
| 579 * Utility function used by `shift()`, `splice()`, and `unshift()`. |
| 580 * |
| 581 * @private |
| 582 * @param {Number} start The index to start inserting elements. |
| 583 * @param {Number} deleteCount The number of elements to delete from the inser
t point. |
| 584 * @param {Array} elements The elements to insert. |
| 585 * @returns {Array} An array of deleted elements. |
| 586 */ |
| 587 function insert(start, deleteCount, elements) { |
| 588 // `result` should have its length set to the `deleteCount` |
| 589 // see https://bugs.ecmascript.org/show_bug.cgi?id=332 |
| 590 var deleteEnd = start + deleteCount, |
| 591 elementCount = elements ? elements.length : 0, |
| 592 index = start - 1, |
| 593 length = start + elementCount, |
| 594 object = this, |
| 595 result = Array(deleteCount), |
| 596 tail = slice.call(object, deleteEnd); |
| 597 |
| 598 // delete elements from the array |
| 599 while (++index < deleteEnd) { |
| 600 if (index in object) { |
| 601 result[index - start] = object[index]; |
| 602 delete object[index]; |
| 603 } |
| 604 } |
| 605 // insert elements |
| 606 index = start - 1; |
| 607 while (++index < length) { |
| 608 object[index] = elements[index - start]; |
| 609 } |
| 610 // append tail elements |
| 611 start = index--; |
| 612 length = max(0, (object.length >>> 0) - deleteCount + elementCount); |
| 613 while (++index < length) { |
| 614 if ((index - start) in tail) { |
| 615 object[index] = tail[index - start]; |
| 616 } else if (index in object) { |
| 617 delete object[index]; |
| 618 } |
| 619 } |
| 620 // delete excess elements |
| 621 deleteCount = deleteCount > elementCount ? deleteCount - elementCount : 0; |
| 622 while (deleteCount--) { |
| 623 index = length + deleteCount; |
| 624 if (index in object) { |
| 625 delete object[index]; |
| 626 } |
| 627 } |
| 628 object.length = length; |
| 629 return result; |
| 630 } |
| 631 |
| 632 /** |
| 633 * Rearrange the host array's elements in reverse order. |
| 634 * |
| 635 * @memberOf Benchmark.Suite |
| 636 * @returns {Array} The reversed array. |
| 637 */ |
| 638 function reverse() { |
| 639 var upperIndex, |
| 640 value, |
| 641 index = -1, |
| 642 object = Object(this), |
| 643 length = object.length >>> 0, |
| 644 middle = floor(length / 2); |
| 645 |
| 646 if (length > 1) { |
| 647 while (++index < middle) { |
| 648 upperIndex = length - index - 1; |
| 649 value = upperIndex in object ? object[upperIndex] : uid; |
| 650 if (index in object) { |
| 651 object[upperIndex] = object[index]; |
| 652 } else { |
| 653 delete object[upperIndex]; |
| 654 } |
| 655 if (value != uid) { |
| 656 object[index] = value; |
| 657 } else { |
| 658 delete object[index]; |
| 659 } |
| 660 } |
| 661 } |
| 662 return object; |
| 663 } |
| 664 |
| 665 /** |
| 666 * Removes the first element of the host array and returns it. |
| 667 * |
| 668 * @memberOf Benchmark.Suite |
| 669 * @returns {Mixed} The first element of the array. |
| 670 */ |
| 671 function shift() { |
| 672 return insert.call(this, 0, 1)[0]; |
| 673 } |
| 674 |
| 675 /** |
| 676 * Creates an array of the host array's elements from the start index up to, |
| 677 * but not including, the end index. |
| 678 * |
| 679 * @memberOf Benchmark.Suite |
| 680 * @param {Number} start The starting index. |
| 681 * @param {Number} end The end index. |
| 682 * @returns {Array} The new array. |
| 683 */ |
| 684 function slice(start, end) { |
| 685 var index = -1, |
| 686 object = Object(this), |
| 687 length = object.length >>> 0, |
| 688 result = []; |
| 689 |
| 690 start = toInteger(start); |
| 691 start = start < 0 ? max(length + start, 0) : min(start, length); |
| 692 start--; |
| 693 end = end == null ? length : toInteger(end); |
| 694 end = end < 0 ? max(length + end, 0) : min(end, length); |
| 695 |
| 696 while ((++index, ++start) < end) { |
| 697 if (start in object) { |
| 698 result[index] = object[start]; |
| 699 } |
| 700 } |
| 701 return result; |
| 702 } |
| 703 |
| 704 /** |
| 705 * Allows removing a range of elements and/or inserting elements into the |
| 706 * host array. |
| 707 * |
| 708 * @memberOf Benchmark.Suite |
| 709 * @param {Number} start The start index. |
| 710 * @param {Number} deleteCount The number of elements to delete. |
| 711 * @param {Mixed} [val1, val2, ...] values to insert at the `start` index. |
| 712 * @returns {Array} An array of removed elements. |
| 713 */ |
| 714 function splice(start, deleteCount) { |
| 715 var object = Object(this), |
| 716 length = object.length >>> 0; |
| 717 |
| 718 start = toInteger(start); |
| 719 start = start < 0 ? max(length + start, 0) : min(start, length); |
| 720 |
| 721 // support the de-facto SpiderMonkey extension |
| 722 // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Arra
y/splice#Parameters |
| 723 // https://bugs.ecmascript.org/show_bug.cgi?id=429 |
| 724 deleteCount = arguments.length == 1 |
| 725 ? length - start |
| 726 : min(max(toInteger(deleteCount), 0), length - start); |
| 727 |
| 728 return insert.call(object, start, deleteCount, slice.call(arguments, 2)); |
| 729 } |
| 730 |
| 731 /** |
| 732 * Converts the specified `value` to an integer. |
| 733 * |
| 734 * @private |
| 735 * @param {Mixed} value The value to convert. |
| 736 * @returns {Number} The resulting integer. |
| 737 */ |
| 738 function toInteger(value) { |
| 739 value = +value; |
| 740 return value === 0 || !isFinite(value) ? value || 0 : value - (value % 1); |
| 741 } |
| 742 |
| 743 /** |
| 744 * Appends arguments to the host array. |
| 745 * |
| 746 * @memberOf Benchmark.Suite |
| 747 * @returns {Number} The new length. |
| 748 */ |
| 749 function unshift() { |
| 750 var object = Object(this); |
| 751 insert.call(object, 0, 0, arguments); |
| 752 return object.length; |
| 753 } |
| 754 |
| 755 /*--------------------------------------------------------------------------*/ |
| 756 |
| 757 /** |
| 758 * A generic `Function#bind` like method. |
| 759 * |
| 760 * @private |
| 761 * @param {Function} fn The function to be bound to `thisArg`. |
| 762 * @param {Mixed} thisArg The `this` binding for the given function. |
| 763 * @returns {Function} The bound function. |
| 764 */ |
| 765 function bind(fn, thisArg) { |
| 766 return function() { fn.apply(thisArg, arguments); }; |
| 767 } |
| 768 |
| 769 /** |
| 770 * Creates a function from the given arguments string and body. |
| 771 * |
| 772 * @private |
| 773 * @param {String} args The comma separated function arguments. |
| 774 * @param {String} body The function body. |
| 775 * @returns {Function} The new function. |
| 776 */ |
| 777 function createFunction() { |
| 778 // lazy define |
| 779 createFunction = function(args, body) { |
| 780 var result, |
| 781 anchor = freeDefine ? define.amd : Benchmark, |
| 782 prop = uid + 'createFunction'; |
| 783 |
| 784 runScript((freeDefine ? 'define.amd.' : 'Benchmark.') + prop + '=function(
' + args + '){' + body + '}'); |
| 785 result = anchor[prop]; |
| 786 delete anchor[prop]; |
| 787 return result; |
| 788 }; |
| 789 // fix JaegerMonkey bug |
| 790 // http://bugzil.la/639720 |
| 791 createFunction = support.browser && (createFunction('', 'return"' + uid + '"
') || noop)() == uid ? createFunction : Function; |
| 792 return createFunction.apply(null, arguments); |
| 793 } |
| 794 |
| 795 /** |
| 796 * Delay the execution of a function based on the benchmark's `delay` property
. |
| 797 * |
| 798 * @private |
| 799 * @param {Object} bench The benchmark instance. |
| 800 * @param {Object} fn The function to execute. |
| 801 */ |
| 802 function delay(bench, fn) { |
| 803 bench._timerId = setTimeout(fn, bench.delay * 1e3); |
| 804 } |
| 805 |
| 806 /** |
| 807 * Destroys the given element. |
| 808 * |
| 809 * @private |
| 810 * @param {Element} element The element to destroy. |
| 811 */ |
| 812 function destroyElement(element) { |
| 813 trash.appendChild(element); |
| 814 trash.innerHTML = ''; |
| 815 } |
| 816 |
| 817 /** |
| 818 * Iterates over an object's properties, executing the `callback` for each. |
| 819 * Callbacks may terminate the loop by explicitly returning `false`. |
| 820 * |
| 821 * @private |
| 822 * @param {Object} object The object to iterate over. |
| 823 * @param {Function} callback The function executed per own property. |
| 824 * @param {Object} options The options object. |
| 825 * @returns {Object} Returns the object iterated over. |
| 826 */ |
| 827 function forProps() { |
| 828 var forShadowed, |
| 829 skipSeen, |
| 830 forArgs = true, |
| 831 shadowed = ['constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyI
sEnumerable', 'toLocaleString', 'toString', 'valueOf']; |
| 832 |
| 833 (function(enumFlag, key) { |
| 834 // must use a non-native constructor to catch the Safari 2 issue |
| 835 function Klass() { this.valueOf = 0; }; |
| 836 Klass.prototype.valueOf = 0; |
| 837 // check various for-in bugs |
| 838 for (key in new Klass) { |
| 839 enumFlag += key == 'valueOf' ? 1 : 0; |
| 840 } |
| 841 // check if `arguments` objects have non-enumerable indexes |
| 842 for (key in arguments) { |
| 843 key == '0' && (forArgs = false); |
| 844 } |
| 845 // Safari 2 iterates over shadowed properties twice |
| 846 // http://replay.waybackmachine.org/20090428222941/http://tobielangel.com/
2007/1/29/for-in-loop-broken-in-safari/ |
| 847 skipSeen = enumFlag == 2; |
| 848 // IE < 9 incorrectly makes an object's properties non-enumerable if they
have |
| 849 // the same name as other non-enumerable properties in its prototype chain
. |
| 850 forShadowed = !enumFlag; |
| 851 }(0)); |
| 852 |
| 853 // lazy define |
| 854 forProps = function(object, callback, options) { |
| 855 options || (options = {}); |
| 856 |
| 857 var result = object; |
| 858 object = Object(object); |
| 859 |
| 860 var ctor, |
| 861 key, |
| 862 keys, |
| 863 skipCtor, |
| 864 done = !result, |
| 865 which = options.which, |
| 866 allFlag = which == 'all', |
| 867 index = -1, |
| 868 iteratee = object, |
| 869 length = object.length, |
| 870 ownFlag = allFlag || which == 'own', |
| 871 seen = {}, |
| 872 skipProto = isClassOf(object, 'Function'), |
| 873 thisArg = options.bind; |
| 874 |
| 875 if (thisArg !== undefined) { |
| 876 callback = bind(callback, thisArg); |
| 877 } |
| 878 // iterate all properties |
| 879 if (allFlag && support.getAllKeys) { |
| 880 for (index = 0, keys = getAllKeys(object), length = keys.length; index <
length; index++) { |
| 881 key = keys[index]; |
| 882 if (callback(object[key], key, object) === false) { |
| 883 break; |
| 884 } |
| 885 } |
| 886 } |
| 887 // else iterate only enumerable properties |
| 888 else { |
| 889 for (key in object) { |
| 890 // Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1 |
| 891 // (if the prototype or a property on the prototype has been set) |
| 892 // incorrectly set a function's `prototype` property [[Enumerable]] va
lue |
| 893 // to `true`. Because of this we standardize on skipping the `prototyp
e` |
| 894 // property of functions regardless of their [[Enumerable]] value. |
| 895 if ((done = |
| 896 !(skipProto && key == 'prototype') && |
| 897 !(skipSeen && (hasKey(seen, key) || !(seen[key] = true))) && |
| 898 (!ownFlag || ownFlag && hasKey(object, key)) && |
| 899 callback(object[key], key, object) === false)) { |
| 900 break; |
| 901 } |
| 902 } |
| 903 // in IE < 9 strings don't support accessing characters by index |
| 904 if (!done && (forArgs && isArguments(object) || |
| 905 ((noCharByIndex || noCharByOwnIndex) && isClassOf(object, 'String')
&& |
| 906 (iteratee = noCharByIndex ? object.split('') : object)))) { |
| 907 while (++index < length) { |
| 908 if ((done = |
| 909 callback(iteratee[index], String(index), object) === false)) { |
| 910 break; |
| 911 } |
| 912 } |
| 913 } |
| 914 if (!done && forShadowed) { |
| 915 // Because IE < 9 can't set the `[[Enumerable]]` attribute of an exist
ing |
| 916 // property and the `constructor` property of a prototype defaults to |
| 917 // non-enumerable, we manually skip the `constructor` property when we |
| 918 // think we are iterating over a `prototype` object. |
| 919 ctor = object.constructor; |
| 920 skipCtor = ctor && ctor.prototype && ctor.prototype.constructor === ct
or; |
| 921 for (index = 0; index < 7; index++) { |
| 922 key = shadowed[index]; |
| 923 if (!(skipCtor && key == 'constructor') && |
| 924 hasKey(object, key) && |
| 925 callback(object[key], key, object) === false) { |
| 926 break; |
| 927 } |
| 928 } |
| 929 } |
| 930 } |
| 931 return result; |
| 932 }; |
| 933 return forProps.apply(null, arguments); |
| 934 } |
| 935 |
| 936 /** |
| 937 * Gets the name of the first argument from a function's source. |
| 938 * |
| 939 * @private |
| 940 * @param {Function} fn The function. |
| 941 * @returns {String} The argument name. |
| 942 */ |
| 943 function getFirstArgument(fn) { |
| 944 return (!hasKey(fn, 'toString') && |
| 945 (/^[\s(]*function[^(]*\(([^\s,)]+)/.exec(fn) || 0)[1]) || ''; |
| 946 } |
| 947 |
| 948 /** |
| 949 * Computes the arithmetic mean of a sample. |
| 950 * |
| 951 * @private |
| 952 * @param {Array} sample The sample. |
| 953 * @returns {Number} The mean. |
| 954 */ |
| 955 function getMean(sample) { |
| 956 return reduce(sample, function(sum, x) { |
| 957 return sum + x; |
| 958 }) / sample.length || 0; |
| 959 } |
| 960 |
| 961 /** |
| 962 * Gets the source code of a function. |
| 963 * |
| 964 * @private |
| 965 * @param {Function} fn The function. |
| 966 * @param {String} altSource A string used when a function's source code is un
retrievable. |
| 967 * @returns {String} The function's source code. |
| 968 */ |
| 969 function getSource(fn, altSource) { |
| 970 var result = altSource; |
| 971 if (isStringable(fn)) { |
| 972 result = String(fn); |
| 973 } else if (support.decompilation) { |
| 974 // escape the `{` for Firefox 1 |
| 975 result = (/^[^{]+\{([\s\S]*)}\s*$/.exec(fn) || 0)[1]; |
| 976 } |
| 977 // trim string |
| 978 result = (result || '').replace(/^\s+|\s+$/g, ''); |
| 979 |
| 980 // detect strings containing only the "use strict" directive |
| 981 return /^(?:\/\*+[\w|\W]*?\*\/|\/\/.*?[\n\r\u2028\u2029]|\s)*(["'])use stric
t\1;?$/.test(result) |
| 982 ? '' |
| 983 : result; |
| 984 } |
| 985 |
| 986 /** |
| 987 * Checks if a value is an `arguments` object. |
| 988 * |
| 989 * @private |
| 990 * @param {Mixed} value The value to check. |
| 991 * @returns {Boolean} Returns `true` if the value is an `arguments` object, el
se `false`. |
| 992 */ |
| 993 function isArguments() { |
| 994 // lazy define |
| 995 isArguments = function(value) { |
| 996 return toString.call(value) == '[object Arguments]'; |
| 997 }; |
| 998 if (noArgumentsClass) { |
| 999 isArguments = function(value) { |
| 1000 return hasKey(value, 'callee') && |
| 1001 !(propertyIsEnumerable && propertyIsEnumerable.call(value, 'callee')); |
| 1002 }; |
| 1003 } |
| 1004 return isArguments(arguments[0]); |
| 1005 } |
| 1006 |
| 1007 /** |
| 1008 * Checks if an object is of the specified class. |
| 1009 * |
| 1010 * @private |
| 1011 * @param {Mixed} value The value to check. |
| 1012 * @param {String} name The name of the class. |
| 1013 * @returns {Boolean} Returns `true` if the value is of the specified class, e
lse `false`. |
| 1014 */ |
| 1015 function isClassOf(value, name) { |
| 1016 return value != null && toString.call(value) == '[object ' + name + ']'; |
| 1017 } |
| 1018 |
| 1019 /** |
| 1020 * Host objects can return type values that are different from their actual |
| 1021 * data type. The objects we are concerned with usually return non-primitive |
| 1022 * types of object, function, or unknown. |
| 1023 * |
| 1024 * @private |
| 1025 * @param {Mixed} object The owner of the property. |
| 1026 * @param {String} property The property to check. |
| 1027 * @returns {Boolean} Returns `true` if the property value is a non-primitive,
else `false`. |
| 1028 */ |
| 1029 function isHostType(object, property) { |
| 1030 var type = object != null ? typeof object[property] : 'number'; |
| 1031 return !/^(?:boolean|number|string|undefined)$/.test(type) && |
| 1032 (type == 'object' ? !!object[property] : true); |
| 1033 } |
| 1034 |
| 1035 /** |
| 1036 * Checks if a given `value` is an object created by the `Object` constructor |
| 1037 * assuming objects created by the `Object` constructor have no inherited |
| 1038 * enumerable properties and that there are no `Object.prototype` extensions. |
| 1039 * |
| 1040 * @private |
| 1041 * @param {Mixed} value The value to check. |
| 1042 * @returns {Boolean} Returns `true` if the `value` is a plain `Object` object
, else `false`. |
| 1043 */ |
| 1044 function isPlainObject(value) { |
| 1045 // avoid non-objects and false positives for `arguments` objects in IE < 9 |
| 1046 var result = false; |
| 1047 if (!(value && typeof value == 'object') || (noArgumentsClass && isArguments
(value))) { |
| 1048 return result; |
| 1049 } |
| 1050 // IE < 9 presents DOM nodes as `Object` objects except they have `toString` |
| 1051 // methods that are `typeof` "string" and still can coerce nodes to strings. |
| 1052 // Also check that the constructor is `Object` (i.e. `Object instanceof Obje
ct`) |
| 1053 var ctor = value.constructor; |
| 1054 if ((support.nodeClass || !(typeof value.toString != 'function' && typeof (v
alue + '') == 'string')) && |
| 1055 (!isClassOf(ctor, 'Function') || ctor instanceof ctor)) { |
| 1056 // In most environments an object's own properties are iterated before |
| 1057 // its inherited properties. If the last iterated property is an object's |
| 1058 // own property then there are no inherited enumerable properties. |
| 1059 if (support.iteratesOwnFirst) { |
| 1060 forProps(value, function(subValue, subKey) { |
| 1061 result = subKey; |
| 1062 }); |
| 1063 return result === false || hasKey(value, result); |
| 1064 } |
| 1065 // IE < 9 iterates inherited properties before own properties. If the firs
t |
| 1066 // iterated property is an object's own property then there are no inherit
ed |
| 1067 // enumerable properties. |
| 1068 forProps(value, function(subValue, subKey) { |
| 1069 result = !hasKey(value, subKey); |
| 1070 return false; |
| 1071 }); |
| 1072 return result === false; |
| 1073 } |
| 1074 return result; |
| 1075 } |
| 1076 |
| 1077 /** |
| 1078 * Checks if a value can be safely coerced to a string. |
| 1079 * |
| 1080 * @private |
| 1081 * @param {Mixed} value The value to check. |
| 1082 * @returns {Boolean} Returns `true` if the value can be coerced, else `false`
. |
| 1083 */ |
| 1084 function isStringable(value) { |
| 1085 return hasKey(value, 'toString') || isClassOf(value, 'String'); |
| 1086 } |
| 1087 |
| 1088 /** |
| 1089 * Wraps a function and passes `this` to the original function as the |
| 1090 * first argument. |
| 1091 * |
| 1092 * @private |
| 1093 * @param {Function} fn The function to be wrapped. |
| 1094 * @returns {Function} The new function. |
| 1095 */ |
| 1096 function methodize(fn) { |
| 1097 return function() { |
| 1098 var args = [this]; |
| 1099 args.push.apply(args, arguments); |
| 1100 return fn.apply(null, args); |
| 1101 }; |
| 1102 } |
| 1103 |
| 1104 /** |
| 1105 * A no-operation function. |
| 1106 * |
| 1107 * @private |
| 1108 */ |
| 1109 function noop() { |
| 1110 // no operation performed |
| 1111 } |
| 1112 |
| 1113 /** |
| 1114 * A wrapper around require() to suppress `module missing` errors. |
| 1115 * |
| 1116 * @private |
| 1117 * @param {String} id The module id. |
| 1118 * @returns {Mixed} The exported module or `null`. |
| 1119 */ |
| 1120 function req(id) { |
| 1121 try { |
| 1122 var result = freeExports && freeRequire(id); |
| 1123 } catch(e) { } |
| 1124 return result || null; |
| 1125 } |
| 1126 |
| 1127 /** |
| 1128 * Runs a snippet of JavaScript via script injection. |
| 1129 * |
| 1130 * @private |
| 1131 * @param {String} code The code to run. |
| 1132 */ |
| 1133 function runScript(code) { |
| 1134 var anchor = freeDefine ? define.amd : Benchmark, |
| 1135 script = doc.createElement('script'), |
| 1136 sibling = doc.getElementsByTagName('script')[0], |
| 1137 parent = sibling.parentNode, |
| 1138 prop = uid + 'runScript', |
| 1139 prefix = '(' + (freeDefine ? 'define.amd.' : 'Benchmark.') + prop + '||f
unction(){})();'; |
| 1140 |
| 1141 // Firefox 2.0.0.2 cannot use script injection as intended because it execut
es |
| 1142 // asynchronously, but that's OK because script injection is only used to av
oid |
| 1143 // the previously commented JaegerMonkey bug. |
| 1144 try { |
| 1145 // remove the inserted script *before* running the code to avoid differenc
es |
| 1146 // in the expected script element count/order of the document. |
| 1147 script.appendChild(doc.createTextNode(prefix + code)); |
| 1148 anchor[prop] = function() { destroyElement(script); }; |
| 1149 } catch(e) { |
| 1150 parent = parent.cloneNode(false); |
| 1151 sibling = null; |
| 1152 script.text = code; |
| 1153 } |
| 1154 parent.insertBefore(script, sibling); |
| 1155 delete anchor[prop]; |
| 1156 } |
| 1157 |
| 1158 /** |
| 1159 * A helper function for setting options/event handlers. |
| 1160 * |
| 1161 * @private |
| 1162 * @param {Object} bench The benchmark instance. |
| 1163 * @param {Object} [options={}] Options object. |
| 1164 */ |
| 1165 function setOptions(bench, options) { |
| 1166 options = extend({}, bench.constructor.options, options); |
| 1167 bench.options = forOwn(options, function(value, key) { |
| 1168 if (value != null) { |
| 1169 // add event listeners |
| 1170 if (/^on[A-Z]/.test(key)) { |
| 1171 forEach(key.split(' '), function(key) { |
| 1172 bench.on(key.slice(2).toLowerCase(), value); |
| 1173 }); |
| 1174 } else if (!hasKey(bench, key)) { |
| 1175 bench[key] = deepClone(value); |
| 1176 } |
| 1177 } |
| 1178 }); |
| 1179 } |
| 1180 |
| 1181 /*--------------------------------------------------------------------------*/ |
| 1182 |
| 1183 /** |
| 1184 * Handles cycling/completing the deferred benchmark. |
| 1185 * |
| 1186 * @memberOf Benchmark.Deferred |
| 1187 */ |
| 1188 function resolve() { |
| 1189 var me = this, |
| 1190 clone = me.benchmark, |
| 1191 bench = clone._original; |
| 1192 |
| 1193 if (bench.aborted) { |
| 1194 // cycle() -> clone cycle/complete event -> compute()'s invoked bench.run(
) cycle/complete |
| 1195 me.teardown(); |
| 1196 clone.running = false; |
| 1197 cycle(me); |
| 1198 } |
| 1199 else if (++me.cycles < clone.count) { |
| 1200 // continue the test loop |
| 1201 if (support.timeout) { |
| 1202 // use setTimeout to avoid a call stack overflow if called recursively |
| 1203 setTimeout(function() { clone.compiled.call(me, timer); }, 0); |
| 1204 } else { |
| 1205 clone.compiled.call(me, timer); |
| 1206 } |
| 1207 } |
| 1208 else { |
| 1209 timer.stop(me); |
| 1210 me.teardown(); |
| 1211 delay(clone, function() { cycle(me); }); |
| 1212 } |
| 1213 } |
| 1214 |
| 1215 /*--------------------------------------------------------------------------*/ |
| 1216 |
| 1217 /** |
| 1218 * A deep clone utility. |
| 1219 * |
| 1220 * @static |
| 1221 * @memberOf Benchmark |
| 1222 * @param {Mixed} value The value to clone. |
| 1223 * @returns {Mixed} The cloned value. |
| 1224 */ |
| 1225 function deepClone(value) { |
| 1226 var accessor, |
| 1227 circular, |
| 1228 clone, |
| 1229 ctor, |
| 1230 descriptor, |
| 1231 extensible, |
| 1232 key, |
| 1233 length, |
| 1234 markerKey, |
| 1235 parent, |
| 1236 result, |
| 1237 source, |
| 1238 subIndex, |
| 1239 data = { 'value': value }, |
| 1240 index = 0, |
| 1241 marked = [], |
| 1242 queue = { 'length': 0 }, |
| 1243 unmarked = []; |
| 1244 |
| 1245 /** |
| 1246 * An easily detectable decorator for cloned values. |
| 1247 */ |
| 1248 function Marker(object) { |
| 1249 this.raw = object; |
| 1250 } |
| 1251 |
| 1252 /** |
| 1253 * The callback used by `forProps()`. |
| 1254 */ |
| 1255 function forPropsCallback(subValue, subKey) { |
| 1256 // exit early to avoid cloning the marker |
| 1257 if (subValue && subValue.constructor == Marker) { |
| 1258 return; |
| 1259 } |
| 1260 // add objects to the queue |
| 1261 if (subValue === Object(subValue)) { |
| 1262 queue[queue.length++] = { 'key': subKey, 'parent': clone, 'source': valu
e }; |
| 1263 } |
| 1264 // assign non-objects |
| 1265 else { |
| 1266 try { |
| 1267 // will throw an error in strict mode if the property is read-only |
| 1268 clone[subKey] = subValue; |
| 1269 } catch(e) { } |
| 1270 } |
| 1271 } |
| 1272 |
| 1273 /** |
| 1274 * Gets an available marker key for the given object. |
| 1275 */ |
| 1276 function getMarkerKey(object) { |
| 1277 // avoid collisions with existing keys |
| 1278 var result = uid; |
| 1279 while (object[result] && object[result].constructor != Marker) { |
| 1280 result += 1; |
| 1281 } |
| 1282 return result; |
| 1283 } |
| 1284 |
| 1285 do { |
| 1286 key = data.key; |
| 1287 parent = data.parent; |
| 1288 source = data.source; |
| 1289 clone = value = source ? source[key] : data.value; |
| 1290 accessor = circular = descriptor = false; |
| 1291 |
| 1292 // create a basic clone to filter out functions, DOM elements, and |
| 1293 // other non `Object` objects |
| 1294 if (value === Object(value)) { |
| 1295 // use custom deep clone function if available |
| 1296 if (isClassOf(value.deepClone, 'Function')) { |
| 1297 clone = value.deepClone(); |
| 1298 } else { |
| 1299 ctor = value.constructor; |
| 1300 switch (toString.call(value)) { |
| 1301 case '[object Array]': |
| 1302 clone = new ctor(value.length); |
| 1303 break; |
| 1304 |
| 1305 case '[object Boolean]': |
| 1306 clone = new ctor(value == true); |
| 1307 break; |
| 1308 |
| 1309 case '[object Date]': |
| 1310 clone = new ctor(+value); |
| 1311 break; |
| 1312 |
| 1313 case '[object Object]': |
| 1314 isPlainObject(value) && (clone = {}); |
| 1315 break; |
| 1316 |
| 1317 case '[object Number]': |
| 1318 case '[object String]': |
| 1319 clone = new ctor(value); |
| 1320 break; |
| 1321 |
| 1322 case '[object RegExp]': |
| 1323 clone = ctor(value.source, |
| 1324 (value.global ? 'g' : '') + |
| 1325 (value.ignoreCase ? 'i' : '') + |
| 1326 (value.multiline ? 'm' : '')); |
| 1327 } |
| 1328 } |
| 1329 // continue clone if `value` doesn't have an accessor descriptor |
| 1330 // http://es5.github.com/#x8.10.1 |
| 1331 if (clone && clone != value && |
| 1332 !(descriptor = source && support.descriptors && getDescriptor(source
, key), |
| 1333 accessor = descriptor && (descriptor.get || descriptor.set))) { |
| 1334 // use an existing clone (circular reference) |
| 1335 if ((extensible = isExtensible(value))) { |
| 1336 markerKey = getMarkerKey(value); |
| 1337 if (value[markerKey]) { |
| 1338 circular = clone = value[markerKey].raw; |
| 1339 } |
| 1340 } else { |
| 1341 // for frozen/sealed objects |
| 1342 for (subIndex = 0, length = unmarked.length; subIndex < length; subI
ndex++) { |
| 1343 data = unmarked[subIndex]; |
| 1344 if (data.object === value) { |
| 1345 circular = clone = data.clone; |
| 1346 break; |
| 1347 } |
| 1348 } |
| 1349 } |
| 1350 if (!circular) { |
| 1351 // mark object to allow quickly detecting circular references and ti
e it to its clone |
| 1352 if (extensible) { |
| 1353 value[markerKey] = new Marker(clone); |
| 1354 marked.push({ 'key': markerKey, 'object': value }); |
| 1355 } else { |
| 1356 // for frozen/sealed objects |
| 1357 unmarked.push({ 'clone': clone, 'object': value }); |
| 1358 } |
| 1359 // iterate over object properties |
| 1360 forProps(value, forPropsCallback, { 'which': 'all' }); |
| 1361 } |
| 1362 } |
| 1363 } |
| 1364 if (parent) { |
| 1365 // for custom property descriptors |
| 1366 if (accessor || (descriptor && !(descriptor.configurable && descriptor.e
numerable && descriptor.writable))) { |
| 1367 if ('value' in descriptor) { |
| 1368 descriptor.value = clone; |
| 1369 } |
| 1370 setDescriptor(parent, key, descriptor); |
| 1371 } |
| 1372 // for default property descriptors |
| 1373 else { |
| 1374 parent[key] = clone; |
| 1375 } |
| 1376 } else { |
| 1377 result = clone; |
| 1378 } |
| 1379 } while ((data = queue[index++])); |
| 1380 |
| 1381 // remove markers |
| 1382 for (index = 0, length = marked.length; index < length; index++) { |
| 1383 data = marked[index]; |
| 1384 delete data.object[data.key]; |
| 1385 } |
| 1386 return result; |
| 1387 } |
| 1388 |
| 1389 /** |
| 1390 * An iteration utility for arrays and objects. |
| 1391 * Callbacks may terminate the loop by explicitly returning `false`. |
| 1392 * |
| 1393 * @static |
| 1394 * @memberOf Benchmark |
| 1395 * @param {Array|Object} object The object to iterate over. |
| 1396 * @param {Function} callback The function called per iteration. |
| 1397 * @param {Mixed} thisArg The `this` binding for the callback. |
| 1398 * @returns {Array|Object} Returns the object iterated over. |
| 1399 */ |
| 1400 function each(object, callback, thisArg) { |
| 1401 var result = object; |
| 1402 object = Object(object); |
| 1403 |
| 1404 var fn = callback, |
| 1405 index = -1, |
| 1406 length = object.length, |
| 1407 isSnapshot = !!(object.snapshotItem && (length = object.snapshotLength))
, |
| 1408 isSplittable = (noCharByIndex || noCharByOwnIndex) && isClassOf(object,
'String'), |
| 1409 isConvertable = isSnapshot || isSplittable || 'item' in object, |
| 1410 origObject = object; |
| 1411 |
| 1412 // in Opera < 10.5 `hasKey(object, 'length')` returns `false` for NodeLists |
| 1413 if (length === length >>> 0) { |
| 1414 if (isConvertable) { |
| 1415 // the third argument of the callback is the original non-array object |
| 1416 callback = function(value, index) { |
| 1417 return fn.call(this, value, index, origObject); |
| 1418 }; |
| 1419 // in IE < 9 strings don't support accessing characters by index |
| 1420 if (isSplittable) { |
| 1421 object = object.split(''); |
| 1422 } else { |
| 1423 object = []; |
| 1424 while (++index < length) { |
| 1425 // in Safari 2 `index in object` is always `false` for NodeLists |
| 1426 object[index] = isSnapshot ? result.snapshotItem(index) : result[ind
ex]; |
| 1427 } |
| 1428 } |
| 1429 } |
| 1430 forEach(object, callback, thisArg); |
| 1431 } else { |
| 1432 forOwn(object, callback, thisArg); |
| 1433 } |
| 1434 return result; |
| 1435 } |
| 1436 |
| 1437 /** |
| 1438 * Copies enumerable properties from the source(s) object to the destination o
bject. |
| 1439 * |
| 1440 * @static |
| 1441 * @memberOf Benchmark |
| 1442 * @param {Object} destination The destination object. |
| 1443 * @param {Object} [source={}] The source object. |
| 1444 * @returns {Object} The destination object. |
| 1445 */ |
| 1446 function extend(destination, source) { |
| 1447 // Chrome < 14 incorrectly sets `destination` to `undefined` when we `delete
arguments[0]` |
| 1448 // http://code.google.com/p/v8/issues/detail?id=839 |
| 1449 var result = destination; |
| 1450 delete arguments[0]; |
| 1451 |
| 1452 forEach(arguments, function(source) { |
| 1453 forProps(source, function(value, key) { |
| 1454 result[key] = value; |
| 1455 }); |
| 1456 }); |
| 1457 return result; |
| 1458 } |
| 1459 |
| 1460 /** |
| 1461 * A generic `Array#filter` like method. |
| 1462 * |
| 1463 * @static |
| 1464 * @memberOf Benchmark |
| 1465 * @param {Array} array The array to iterate over. |
| 1466 * @param {Function|String} callback The function/alias called per iteration. |
| 1467 * @param {Mixed} thisArg The `this` binding for the callback. |
| 1468 * @returns {Array} A new array of values that passed callback filter. |
| 1469 * @example |
| 1470 * |
| 1471 * // get odd numbers |
| 1472 * Benchmark.filter([1, 2, 3, 4, 5], function(n) { |
| 1473 * return n % 2; |
| 1474 * }); // -> [1, 3, 5]; |
| 1475 * |
| 1476 * // get fastest benchmarks |
| 1477 * Benchmark.filter(benches, 'fastest'); |
| 1478 * |
| 1479 * // get slowest benchmarks |
| 1480 * Benchmark.filter(benches, 'slowest'); |
| 1481 * |
| 1482 * // get benchmarks that completed without erroring |
| 1483 * Benchmark.filter(benches, 'successful'); |
| 1484 */ |
| 1485 function filter(array, callback, thisArg) { |
| 1486 var result; |
| 1487 |
| 1488 if (callback == 'successful') { |
| 1489 // callback to exclude those that are errored, unrun, or have hz of Infini
ty |
| 1490 callback = function(bench) { return bench.cycles && isFinite(bench.hz); }; |
| 1491 } |
| 1492 else if (callback == 'fastest' || callback == 'slowest') { |
| 1493 // get successful, sort by period + margin of error, and filter fastest/sl
owest |
| 1494 result = filter(array, 'successful').sort(function(a, b) { |
| 1495 a = a.stats; b = b.stats; |
| 1496 return (a.mean + a.moe > b.mean + b.moe ? 1 : -1) * (callback == 'fastes
t' ? 1 : -1); |
| 1497 }); |
| 1498 result = filter(result, function(bench) { |
| 1499 return result[0].compare(bench) == 0; |
| 1500 }); |
| 1501 } |
| 1502 return result || reduce(array, function(result, value, index) { |
| 1503 return callback.call(thisArg, value, index, array) ? (result.push(value),
result) : result; |
| 1504 }, []); |
| 1505 } |
| 1506 |
| 1507 /** |
| 1508 * A generic `Array#forEach` like method. |
| 1509 * Callbacks may terminate the loop by explicitly returning `false`. |
| 1510 * |
| 1511 * @static |
| 1512 * @memberOf Benchmark |
| 1513 * @param {Array} array The array to iterate over. |
| 1514 * @param {Function} callback The function called per iteration. |
| 1515 * @param {Mixed} thisArg The `this` binding for the callback. |
| 1516 * @returns {Array} Returns the array iterated over. |
| 1517 */ |
| 1518 function forEach(array, callback, thisArg) { |
| 1519 var index = -1, |
| 1520 length = (array = Object(array)).length >>> 0; |
| 1521 |
| 1522 if (thisArg !== undefined) { |
| 1523 callback = bind(callback, thisArg); |
| 1524 } |
| 1525 while (++index < length) { |
| 1526 if (index in array && |
| 1527 callback(array[index], index, array) === false) { |
| 1528 break; |
| 1529 } |
| 1530 } |
| 1531 return array; |
| 1532 } |
| 1533 |
| 1534 /** |
| 1535 * Iterates over an object's own properties, executing the `callback` for each
. |
| 1536 * Callbacks may terminate the loop by explicitly returning `false`. |
| 1537 * |
| 1538 * @static |
| 1539 * @memberOf Benchmark |
| 1540 * @param {Object} object The object to iterate over. |
| 1541 * @param {Function} callback The function executed per own property. |
| 1542 * @param {Mixed} thisArg The `this` binding for the callback. |
| 1543 * @returns {Object} Returns the object iterated over. |
| 1544 */ |
| 1545 function forOwn(object, callback, thisArg) { |
| 1546 return forProps(object, callback, { 'bind': thisArg, 'which': 'own' }); |
| 1547 } |
| 1548 |
| 1549 /** |
| 1550 * Converts a number to a more readable comma-separated string representation. |
| 1551 * |
| 1552 * @static |
| 1553 * @memberOf Benchmark |
| 1554 * @param {Number} number The number to convert. |
| 1555 * @returns {String} The more readable string representation. |
| 1556 */ |
| 1557 function formatNumber(number) { |
| 1558 number = String(number).split('.'); |
| 1559 return number[0].replace(/(?=(?:\d{3})+$)(?!\b)/g, ',') + |
| 1560 (number[1] ? '.' + number[1] : ''); |
| 1561 } |
| 1562 |
| 1563 /** |
| 1564 * Checks if an object has the specified key as a direct property. |
| 1565 * |
| 1566 * @static |
| 1567 * @memberOf Benchmark |
| 1568 * @param {Object} object The object to check. |
| 1569 * @param {String} key The key to check for. |
| 1570 * @returns {Boolean} Returns `true` if key is a direct property, else `false`
. |
| 1571 */ |
| 1572 function hasKey() { |
| 1573 // lazy define for worst case fallback (not as accurate) |
| 1574 hasKey = function(object, key) { |
| 1575 var parent = object != null && (object.constructor || Object).prototype; |
| 1576 return !!parent && key in Object(object) && !(key in parent && object[key]
=== parent[key]); |
| 1577 }; |
| 1578 // for modern browsers |
| 1579 if (isClassOf(hasOwnProperty, 'Function')) { |
| 1580 hasKey = function(object, key) { |
| 1581 return object != null && hasOwnProperty.call(object, key); |
| 1582 }; |
| 1583 } |
| 1584 // for Safari 2 |
| 1585 else if ({}.__proto__ == Object.prototype) { |
| 1586 hasKey = function(object, key) { |
| 1587 var result = false; |
| 1588 if (object != null) { |
| 1589 object = Object(object); |
| 1590 object.__proto__ = [object.__proto__, object.__proto__ = null, result
= key in object][0]; |
| 1591 } |
| 1592 return result; |
| 1593 }; |
| 1594 } |
| 1595 return hasKey.apply(this, arguments); |
| 1596 } |
| 1597 |
| 1598 /** |
| 1599 * A generic `Array#indexOf` like method. |
| 1600 * |
| 1601 * @static |
| 1602 * @memberOf Benchmark |
| 1603 * @param {Array} array The array to iterate over. |
| 1604 * @param {Mixed} value The value to search for. |
| 1605 * @param {Number} [fromIndex=0] The index to start searching from. |
| 1606 * @returns {Number} The index of the matched value or `-1`. |
| 1607 */ |
| 1608 function indexOf(array, value, fromIndex) { |
| 1609 var index = toInteger(fromIndex), |
| 1610 length = (array = Object(array)).length >>> 0; |
| 1611 |
| 1612 index = (index < 0 ? max(0, length + index) : index) - 1; |
| 1613 while (++index < length) { |
| 1614 if (index in array && value === array[index]) { |
| 1615 return index; |
| 1616 } |
| 1617 } |
| 1618 return -1; |
| 1619 } |
| 1620 |
| 1621 /** |
| 1622 * Modify a string by replacing named tokens with matching object property val
ues. |
| 1623 * |
| 1624 * @static |
| 1625 * @memberOf Benchmark |
| 1626 * @param {String} string The string to modify. |
| 1627 * @param {Object} object The template object. |
| 1628 * @returns {String} The modified string. |
| 1629 */ |
| 1630 function interpolate(string, object) { |
| 1631 forOwn(object, function(value, key) { |
| 1632 // escape regexp special characters in `key` |
| 1633 string = string.replace(RegExp('#\\{' + key.replace(/([.*+?^=!:${}()|[\]\/
\\])/g, '\\$1') + '\\}', 'g'), value); |
| 1634 }); |
| 1635 return string; |
| 1636 } |
| 1637 |
| 1638 /** |
| 1639 * Invokes a method on all items in an array. |
| 1640 * |
| 1641 * @static |
| 1642 * @memberOf Benchmark |
| 1643 * @param {Array} benches Array of benchmarks to iterate over. |
| 1644 * @param {String|Object} name The name of the method to invoke OR options obj
ect. |
| 1645 * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with. |
| 1646 * @returns {Array} A new array of values returned from each method invoked. |
| 1647 * @example |
| 1648 * |
| 1649 * // invoke `reset` on all benchmarks |
| 1650 * Benchmark.invoke(benches, 'reset'); |
| 1651 * |
| 1652 * // invoke `emit` with arguments |
| 1653 * Benchmark.invoke(benches, 'emit', 'complete', listener); |
| 1654 * |
| 1655 * // invoke `run(true)`, treat benchmarks as a queue, and register invoke cal
lbacks |
| 1656 * Benchmark.invoke(benches, { |
| 1657 * |
| 1658 * // invoke the `run` method |
| 1659 * 'name': 'run', |
| 1660 * |
| 1661 * // pass a single argument |
| 1662 * 'args': true, |
| 1663 * |
| 1664 * // treat as queue, removing benchmarks from front of `benches` until empt
y |
| 1665 * 'queued': true, |
| 1666 * |
| 1667 * // called before any benchmarks have been invoked. |
| 1668 * 'onStart': onStart, |
| 1669 * |
| 1670 * // called between invoking benchmarks |
| 1671 * 'onCycle': onCycle, |
| 1672 * |
| 1673 * // called after all benchmarks have been invoked. |
| 1674 * 'onComplete': onComplete |
| 1675 * }); |
| 1676 */ |
| 1677 function invoke(benches, name) { |
| 1678 var args, |
| 1679 bench, |
| 1680 queued, |
| 1681 index = -1, |
| 1682 eventProps = { 'currentTarget': benches }, |
| 1683 options = { 'onStart': noop, 'onCycle': noop, 'onComplete': noop }, |
| 1684 result = map(benches, function(bench) { return bench; }); |
| 1685 |
| 1686 /** |
| 1687 * Invokes the method of the current object and if synchronous, fetches the
next. |
| 1688 */ |
| 1689 function execute() { |
| 1690 var listeners, |
| 1691 async = isAsync(bench); |
| 1692 |
| 1693 if (async) { |
| 1694 // use `getNext` as the first listener |
| 1695 bench.on('complete', getNext); |
| 1696 listeners = bench.events.complete; |
| 1697 listeners.splice(0, 0, listeners.pop()); |
| 1698 } |
| 1699 // execute method |
| 1700 result[index] = isClassOf(bench && bench[name], 'Function') ? bench[name].
apply(bench, args) : undefined; |
| 1701 // if synchronous return true until finished |
| 1702 return !async && getNext(); |
| 1703 } |
| 1704 |
| 1705 /** |
| 1706 * Fetches the next bench or executes `onComplete` callback. |
| 1707 */ |
| 1708 function getNext(event) { |
| 1709 var cycleEvent, |
| 1710 last = bench, |
| 1711 async = isAsync(last); |
| 1712 |
| 1713 if (async) { |
| 1714 last.off('complete', getNext); |
| 1715 last.emit('complete'); |
| 1716 } |
| 1717 // emit "cycle" event |
| 1718 eventProps.type = 'cycle'; |
| 1719 eventProps.target = last; |
| 1720 cycleEvent = Event(eventProps); |
| 1721 options.onCycle.call(benches, cycleEvent); |
| 1722 |
| 1723 // choose next benchmark if not exiting early |
| 1724 if (!cycleEvent.aborted && raiseIndex() !== false) { |
| 1725 bench = queued ? benches[0] : result[index]; |
| 1726 if (isAsync(bench)) { |
| 1727 delay(bench, execute); |
| 1728 } |
| 1729 else if (async) { |
| 1730 // resume execution if previously asynchronous but now synchronous |
| 1731 while (execute()) { } |
| 1732 } |
| 1733 else { |
| 1734 // continue synchronous execution |
| 1735 return true; |
| 1736 } |
| 1737 } else { |
| 1738 // emit "complete" event |
| 1739 eventProps.type = 'complete'; |
| 1740 options.onComplete.call(benches, Event(eventProps)); |
| 1741 } |
| 1742 // When used as a listener `event.aborted = true` will cancel the rest of |
| 1743 // the "complete" listeners because they were already called above and whe
n |
| 1744 // used as part of `getNext` the `return false` will exit the execution wh
ile-loop. |
| 1745 if (event) { |
| 1746 event.aborted = true; |
| 1747 } else { |
| 1748 return false; |
| 1749 } |
| 1750 } |
| 1751 |
| 1752 /** |
| 1753 * Checks if invoking `Benchmark#run` with asynchronous cycles. |
| 1754 */ |
| 1755 function isAsync(object) { |
| 1756 // avoid using `instanceof` here because of IE memory leak issues with hos
t objects |
| 1757 var async = args[0] && args[0].async; |
| 1758 return Object(object).constructor == Benchmark && name == 'run' && |
| 1759 ((async == null ? object.options.async : async) && support.timeout || ob
ject.defer); |
| 1760 } |
| 1761 |
| 1762 /** |
| 1763 * Raises `index` to the next defined index or returns `false`. |
| 1764 */ |
| 1765 function raiseIndex() { |
| 1766 var length = result.length; |
| 1767 if (queued) { |
| 1768 // if queued remove the previous bench and subsequent skipped non-entrie
s |
| 1769 do { |
| 1770 ++index > 0 && shift.call(benches); |
| 1771 } while ((length = benches.length) && !('0' in benches)); |
| 1772 } |
| 1773 else { |
| 1774 while (++index < length && !(index in result)) { } |
| 1775 } |
| 1776 // if we reached the last index then return `false` |
| 1777 return (queued ? length : index < length) ? index : (index = false); |
| 1778 } |
| 1779 |
| 1780 // juggle arguments |
| 1781 if (isClassOf(name, 'String')) { |
| 1782 // 2 arguments (array, name) |
| 1783 args = slice.call(arguments, 2); |
| 1784 } else { |
| 1785 // 2 arguments (array, options) |
| 1786 options = extend(options, name); |
| 1787 name = options.name; |
| 1788 args = isClassOf(args = 'args' in options ? options.args : [], 'Array') ?
args : [args]; |
| 1789 queued = options.queued; |
| 1790 } |
| 1791 |
| 1792 // start iterating over the array |
| 1793 if (raiseIndex() !== false) { |
| 1794 // emit "start" event |
| 1795 bench = result[index]; |
| 1796 eventProps.type = 'start'; |
| 1797 eventProps.target = bench; |
| 1798 options.onStart.call(benches, Event(eventProps)); |
| 1799 |
| 1800 // end early if the suite was aborted in an "onStart" listener |
| 1801 if (benches.aborted && benches.constructor == Suite && name == 'run') { |
| 1802 // emit "cycle" event |
| 1803 eventProps.type = 'cycle'; |
| 1804 options.onCycle.call(benches, Event(eventProps)); |
| 1805 // emit "complete" event |
| 1806 eventProps.type = 'complete'; |
| 1807 options.onComplete.call(benches, Event(eventProps)); |
| 1808 } |
| 1809 // else start |
| 1810 else { |
| 1811 if (isAsync(bench)) { |
| 1812 delay(bench, execute); |
| 1813 } else { |
| 1814 while (execute()) { } |
| 1815 } |
| 1816 } |
| 1817 } |
| 1818 return result; |
| 1819 } |
| 1820 |
| 1821 /** |
| 1822 * Creates a string of joined array values or object key-value pairs. |
| 1823 * |
| 1824 * @static |
| 1825 * @memberOf Benchmark |
| 1826 * @param {Array|Object} object The object to operate on. |
| 1827 * @param {String} [separator1=','] The separator used between key-value pairs
. |
| 1828 * @param {String} [separator2=': '] The separator used between keys and value
s. |
| 1829 * @returns {String} The joined result. |
| 1830 */ |
| 1831 function join(object, separator1, separator2) { |
| 1832 var result = [], |
| 1833 length = (object = Object(object)).length, |
| 1834 arrayLike = length === length >>> 0; |
| 1835 |
| 1836 separator2 || (separator2 = ': '); |
| 1837 each(object, function(value, key) { |
| 1838 result.push(arrayLike ? value : key + separator2 + value); |
| 1839 }); |
| 1840 return result.join(separator1 || ','); |
| 1841 } |
| 1842 |
| 1843 /** |
| 1844 * A generic `Array#map` like method. |
| 1845 * |
| 1846 * @static |
| 1847 * @memberOf Benchmark |
| 1848 * @param {Array} array The array to iterate over. |
| 1849 * @param {Function} callback The function called per iteration. |
| 1850 * @param {Mixed} thisArg The `this` binding for the callback. |
| 1851 * @returns {Array} A new array of values returned by the callback. |
| 1852 */ |
| 1853 function map(array, callback, thisArg) { |
| 1854 return reduce(array, function(result, value, index) { |
| 1855 result[index] = callback.call(thisArg, value, index, array); |
| 1856 return result; |
| 1857 }, Array(Object(array).length >>> 0)); |
| 1858 } |
| 1859 |
| 1860 /** |
| 1861 * Retrieves the value of a specified property from all items in an array. |
| 1862 * |
| 1863 * @static |
| 1864 * @memberOf Benchmark |
| 1865 * @param {Array} array The array to iterate over. |
| 1866 * @param {String} property The property to pluck. |
| 1867 * @returns {Array} A new array of property values. |
| 1868 */ |
| 1869 function pluck(array, property) { |
| 1870 return map(array, function(object) { |
| 1871 return object == null ? undefined : object[property]; |
| 1872 }); |
| 1873 } |
| 1874 |
| 1875 /** |
| 1876 * A generic `Array#reduce` like method. |
| 1877 * |
| 1878 * @static |
| 1879 * @memberOf Benchmark |
| 1880 * @param {Array} array The array to iterate over. |
| 1881 * @param {Function} callback The function called per iteration. |
| 1882 * @param {Mixed} accumulator Initial value of the accumulator. |
| 1883 * @returns {Mixed} The accumulator. |
| 1884 */ |
| 1885 function reduce(array, callback, accumulator) { |
| 1886 var noaccum = arguments.length < 3; |
| 1887 forEach(array, function(value, index) { |
| 1888 accumulator = noaccum ? (noaccum = false, value) : callback(accumulator, v
alue, index, array); |
| 1889 }); |
| 1890 return accumulator; |
| 1891 } |
| 1892 |
| 1893 /*--------------------------------------------------------------------------*/ |
| 1894 |
| 1895 /** |
| 1896 * Aborts all benchmarks in the suite. |
| 1897 * |
| 1898 * @name abort |
| 1899 * @memberOf Benchmark.Suite |
| 1900 * @returns {Object} The suite instance. |
| 1901 */ |
| 1902 function abortSuite() { |
| 1903 var event, |
| 1904 me = this, |
| 1905 resetting = calledBy.resetSuite; |
| 1906 |
| 1907 if (me.running) { |
| 1908 event = Event('abort'); |
| 1909 me.emit(event); |
| 1910 if (!event.cancelled || resetting) { |
| 1911 // avoid infinite recursion |
| 1912 calledBy.abortSuite = true; |
| 1913 me.reset(); |
| 1914 delete calledBy.abortSuite; |
| 1915 |
| 1916 if (!resetting) { |
| 1917 me.aborted = true; |
| 1918 invoke(me, 'abort'); |
| 1919 } |
| 1920 } |
| 1921 } |
| 1922 return me; |
| 1923 } |
| 1924 |
| 1925 /** |
| 1926 * Adds a test to the benchmark suite. |
| 1927 * |
| 1928 * @memberOf Benchmark.Suite |
| 1929 * @param {String} name A name to identify the benchmark. |
| 1930 * @param {Function|String} fn The test to benchmark. |
| 1931 * @param {Object} [options={}] Options object. |
| 1932 * @returns {Object} The benchmark instance. |
| 1933 * @example |
| 1934 * |
| 1935 * // basic usage |
| 1936 * suite.add(fn); |
| 1937 * |
| 1938 * // or using a name first |
| 1939 * suite.add('foo', fn); |
| 1940 * |
| 1941 * // or with options |
| 1942 * suite.add('foo', fn, { |
| 1943 * 'onCycle': onCycle, |
| 1944 * 'onComplete': onComplete |
| 1945 * }); |
| 1946 * |
| 1947 * // or name and options |
| 1948 * suite.add('foo', { |
| 1949 * 'fn': fn, |
| 1950 * 'onCycle': onCycle, |
| 1951 * 'onComplete': onComplete |
| 1952 * }); |
| 1953 * |
| 1954 * // or options only |
| 1955 * suite.add({ |
| 1956 * 'name': 'foo', |
| 1957 * 'fn': fn, |
| 1958 * 'onCycle': onCycle, |
| 1959 * 'onComplete': onComplete |
| 1960 * }); |
| 1961 */ |
| 1962 function add(name, fn, options) { |
| 1963 var me = this, |
| 1964 bench = Benchmark(name, fn, options), |
| 1965 event = Event({ 'type': 'add', 'target': bench }); |
| 1966 |
| 1967 if (me.emit(event), !event.cancelled) { |
| 1968 me.push(bench); |
| 1969 } |
| 1970 return me; |
| 1971 } |
| 1972 |
| 1973 /** |
| 1974 * Creates a new suite with cloned benchmarks. |
| 1975 * |
| 1976 * @name clone |
| 1977 * @memberOf Benchmark.Suite |
| 1978 * @param {Object} options Options object to overwrite cloned options. |
| 1979 * @returns {Object} The new suite instance. |
| 1980 */ |
| 1981 function cloneSuite(options) { |
| 1982 var me = this, |
| 1983 result = new me.constructor(extend({}, me.options, options)); |
| 1984 |
| 1985 // copy own properties |
| 1986 forOwn(me, function(value, key) { |
| 1987 if (!hasKey(result, key)) { |
| 1988 result[key] = value && isClassOf(value.clone, 'Function') |
| 1989 ? value.clone() |
| 1990 : deepClone(value); |
| 1991 } |
| 1992 }); |
| 1993 return result; |
| 1994 } |
| 1995 |
| 1996 /** |
| 1997 * An `Array#filter` like method. |
| 1998 * |
| 1999 * @name filter |
| 2000 * @memberOf Benchmark.Suite |
| 2001 * @param {Function|String} callback The function/alias called per iteration. |
| 2002 * @returns {Object} A new suite of benchmarks that passed callback filter. |
| 2003 */ |
| 2004 function filterSuite(callback) { |
| 2005 var me = this, |
| 2006 result = new me.constructor; |
| 2007 |
| 2008 result.push.apply(result, filter(me, callback)); |
| 2009 return result; |
| 2010 } |
| 2011 |
| 2012 /** |
| 2013 * Resets all benchmarks in the suite. |
| 2014 * |
| 2015 * @name reset |
| 2016 * @memberOf Benchmark.Suite |
| 2017 * @returns {Object} The suite instance. |
| 2018 */ |
| 2019 function resetSuite() { |
| 2020 var event, |
| 2021 me = this, |
| 2022 aborting = calledBy.abortSuite; |
| 2023 |
| 2024 if (me.running && !aborting) { |
| 2025 // no worries, `resetSuite()` is called within `abortSuite()` |
| 2026 calledBy.resetSuite = true; |
| 2027 me.abort(); |
| 2028 delete calledBy.resetSuite; |
| 2029 } |
| 2030 // reset if the state has changed |
| 2031 else if ((me.aborted || me.running) && |
| 2032 (me.emit(event = Event('reset')), !event.cancelled)) { |
| 2033 me.running = false; |
| 2034 if (!aborting) { |
| 2035 invoke(me, 'reset'); |
| 2036 } |
| 2037 } |
| 2038 return me; |
| 2039 } |
| 2040 |
| 2041 /** |
| 2042 * Runs the suite. |
| 2043 * |
| 2044 * @name run |
| 2045 * @memberOf Benchmark.Suite |
| 2046 * @param {Object} [options={}] Options object. |
| 2047 * @returns {Object} The suite instance. |
| 2048 * @example |
| 2049 * |
| 2050 * // basic usage |
| 2051 * suite.run(); |
| 2052 * |
| 2053 * // or with options |
| 2054 * suite.run({ 'async': true, 'queued': true }); |
| 2055 */ |
| 2056 function runSuite(options) { |
| 2057 var me = this; |
| 2058 |
| 2059 me.reset(); |
| 2060 me.running = true; |
| 2061 options || (options = {}); |
| 2062 |
| 2063 invoke(me, { |
| 2064 'name': 'run', |
| 2065 'args': options, |
| 2066 'queued': options.queued, |
| 2067 'onStart': function(event) { |
| 2068 me.emit(event); |
| 2069 }, |
| 2070 'onCycle': function(event) { |
| 2071 var bench = event.target; |
| 2072 if (bench.error) { |
| 2073 me.emit({ 'type': 'error', 'target': bench }); |
| 2074 } |
| 2075 me.emit(event); |
| 2076 event.aborted = me.aborted; |
| 2077 }, |
| 2078 'onComplete': function(event) { |
| 2079 me.running = false; |
| 2080 me.emit(event); |
| 2081 } |
| 2082 }); |
| 2083 return me; |
| 2084 } |
| 2085 |
| 2086 /*--------------------------------------------------------------------------*/ |
| 2087 |
| 2088 /** |
| 2089 * Executes all registered listeners of the specified event type. |
| 2090 * |
| 2091 * @memberOf Benchmark, Benchmark.Suite |
| 2092 * @param {String|Object} type The event type or object. |
| 2093 * @returns {Mixed} Returns the return value of the last listener executed. |
| 2094 */ |
| 2095 function emit(type) { |
| 2096 var listeners, |
| 2097 me = this, |
| 2098 event = Event(type), |
| 2099 events = me.events, |
| 2100 args = (arguments[0] = event, arguments); |
| 2101 |
| 2102 event.currentTarget || (event.currentTarget = me); |
| 2103 event.target || (event.target = me); |
| 2104 delete event.result; |
| 2105 |
| 2106 if (events && (listeners = hasKey(events, event.type) && events[event.type])
) { |
| 2107 forEach(listeners.slice(), function(listener) { |
| 2108 if ((event.result = listener.apply(me, args)) === false) { |
| 2109 event.cancelled = true; |
| 2110 } |
| 2111 return !event.aborted; |
| 2112 }); |
| 2113 } |
| 2114 return event.result; |
| 2115 } |
| 2116 |
| 2117 /** |
| 2118 * Returns an array of event listeners for a given type that can be manipulate
d |
| 2119 * to add or remove listeners. |
| 2120 * |
| 2121 * @memberOf Benchmark, Benchmark.Suite |
| 2122 * @param {String} type The event type. |
| 2123 * @returns {Array} The listeners array. |
| 2124 */ |
| 2125 function listeners(type) { |
| 2126 var me = this, |
| 2127 events = me.events || (me.events = {}); |
| 2128 |
| 2129 return hasKey(events, type) ? events[type] : (events[type] = []); |
| 2130 } |
| 2131 |
| 2132 /** |
| 2133 * Unregisters a listener for the specified event type(s), |
| 2134 * or unregisters all listeners for the specified event type(s), |
| 2135 * or unregisters all listeners for all event types. |
| 2136 * |
| 2137 * @memberOf Benchmark, Benchmark.Suite |
| 2138 * @param {String} [type] The event type. |
| 2139 * @param {Function} [listener] The function to unregister. |
| 2140 * @returns {Object} The benchmark instance. |
| 2141 * @example |
| 2142 * |
| 2143 * // unregister a listener for an event type |
| 2144 * bench.off('cycle', listener); |
| 2145 * |
| 2146 * // unregister a listener for multiple event types |
| 2147 * bench.off('start cycle', listener); |
| 2148 * |
| 2149 * // unregister all listeners for an event type |
| 2150 * bench.off('cycle'); |
| 2151 * |
| 2152 * // unregister all listeners for multiple event types |
| 2153 * bench.off('start cycle complete'); |
| 2154 * |
| 2155 * // unregister all listeners for all event types |
| 2156 * bench.off(); |
| 2157 */ |
| 2158 function off(type, listener) { |
| 2159 var me = this, |
| 2160 events = me.events; |
| 2161 |
| 2162 events && each(type ? type.split(' ') : events, function(listeners, type) { |
| 2163 var index; |
| 2164 if (typeof listeners == 'string') { |
| 2165 type = listeners; |
| 2166 listeners = hasKey(events, type) && events[type]; |
| 2167 } |
| 2168 if (listeners) { |
| 2169 if (listener) { |
| 2170 index = indexOf(listeners, listener); |
| 2171 if (index > -1) { |
| 2172 listeners.splice(index, 1); |
| 2173 } |
| 2174 } else { |
| 2175 listeners.length = 0; |
| 2176 } |
| 2177 } |
| 2178 }); |
| 2179 return me; |
| 2180 } |
| 2181 |
| 2182 /** |
| 2183 * Registers a listener for the specified event type(s). |
| 2184 * |
| 2185 * @memberOf Benchmark, Benchmark.Suite |
| 2186 * @param {String} type The event type. |
| 2187 * @param {Function} listener The function to register. |
| 2188 * @returns {Object} The benchmark instance. |
| 2189 * @example |
| 2190 * |
| 2191 * // register a listener for an event type |
| 2192 * bench.on('cycle', listener); |
| 2193 * |
| 2194 * // register a listener for multiple event types |
| 2195 * bench.on('start cycle', listener); |
| 2196 */ |
| 2197 function on(type, listener) { |
| 2198 var me = this, |
| 2199 events = me.events || (me.events = {}); |
| 2200 |
| 2201 forEach(type.split(' '), function(type) { |
| 2202 (hasKey(events, type) |
| 2203 ? events[type] |
| 2204 : (events[type] = []) |
| 2205 ).push(listener); |
| 2206 }); |
| 2207 return me; |
| 2208 } |
| 2209 |
| 2210 /*--------------------------------------------------------------------------*/ |
| 2211 |
| 2212 /** |
| 2213 * Aborts the benchmark without recording times. |
| 2214 * |
| 2215 * @memberOf Benchmark |
| 2216 * @returns {Object} The benchmark instance. |
| 2217 */ |
| 2218 function abort() { |
| 2219 var event, |
| 2220 me = this, |
| 2221 resetting = calledBy.reset; |
| 2222 |
| 2223 if (me.running) { |
| 2224 event = Event('abort'); |
| 2225 me.emit(event); |
| 2226 if (!event.cancelled || resetting) { |
| 2227 // avoid infinite recursion |
| 2228 calledBy.abort = true; |
| 2229 me.reset(); |
| 2230 delete calledBy.abort; |
| 2231 |
| 2232 if (support.timeout) { |
| 2233 clearTimeout(me._timerId); |
| 2234 delete me._timerId; |
| 2235 } |
| 2236 if (!resetting) { |
| 2237 me.aborted = true; |
| 2238 me.running = false; |
| 2239 } |
| 2240 } |
| 2241 } |
| 2242 return me; |
| 2243 } |
| 2244 |
| 2245 /** |
| 2246 * Creates a new benchmark using the same test and options. |
| 2247 * |
| 2248 * @memberOf Benchmark |
| 2249 * @param {Object} options Options object to overwrite cloned options. |
| 2250 * @returns {Object} The new benchmark instance. |
| 2251 * @example |
| 2252 * |
| 2253 * var bizarro = bench.clone({ |
| 2254 * 'name': 'doppelganger' |
| 2255 * }); |
| 2256 */ |
| 2257 function clone(options) { |
| 2258 var me = this, |
| 2259 result = new me.constructor(extend({}, me, options)); |
| 2260 |
| 2261 // correct the `options` object |
| 2262 result.options = extend({}, me.options, options); |
| 2263 |
| 2264 // copy own custom properties |
| 2265 forOwn(me, function(value, key) { |
| 2266 if (!hasKey(result, key)) { |
| 2267 result[key] = deepClone(value); |
| 2268 } |
| 2269 }); |
| 2270 return result; |
| 2271 } |
| 2272 |
| 2273 /** |
| 2274 * Determines if a benchmark is faster than another. |
| 2275 * |
| 2276 * @memberOf Benchmark |
| 2277 * @param {Object} other The benchmark to compare. |
| 2278 * @returns {Number} Returns `-1` if slower, `1` if faster, and `0` if indeter
minate. |
| 2279 */ |
| 2280 function compare(other) { |
| 2281 var critical, |
| 2282 zStat, |
| 2283 me = this, |
| 2284 sample1 = me.stats.sample, |
| 2285 sample2 = other.stats.sample, |
| 2286 size1 = sample1.length, |
| 2287 size2 = sample2.length, |
| 2288 maxSize = max(size1, size2), |
| 2289 minSize = min(size1, size2), |
| 2290 u1 = getU(sample1, sample2), |
| 2291 u2 = getU(sample2, sample1), |
| 2292 u = min(u1, u2); |
| 2293 |
| 2294 function getScore(xA, sampleB) { |
| 2295 return reduce(sampleB, function(total, xB) { |
| 2296 return total + (xB > xA ? 0 : xB < xA ? 1 : 0.5); |
| 2297 }, 0); |
| 2298 } |
| 2299 |
| 2300 function getU(sampleA, sampleB) { |
| 2301 return reduce(sampleA, function(total, xA) { |
| 2302 return total + getScore(xA, sampleB); |
| 2303 }, 0); |
| 2304 } |
| 2305 |
| 2306 function getZ(u) { |
| 2307 return (u - ((size1 * size2) / 2)) / sqrt((size1 * size2 * (size1 + size2
+ 1)) / 12); |
| 2308 } |
| 2309 |
| 2310 // exit early if comparing the same benchmark |
| 2311 if (me == other) { |
| 2312 return 0; |
| 2313 } |
| 2314 // reject the null hyphothesis the two samples come from the |
| 2315 // same population (i.e. have the same median) if... |
| 2316 if (size1 + size2 > 30) { |
| 2317 // ...the z-stat is greater than 1.96 or less than -1.96 |
| 2318 // http://www.statisticslectures.com/topics/mannwhitneyu/ |
| 2319 zStat = getZ(u); |
| 2320 return abs(zStat) > 1.96 ? (zStat > 0 ? -1 : 1) : 0; |
| 2321 } |
| 2322 // ...the U value is less than or equal the critical U value |
| 2323 // http://www.geoib.com/mann-whitney-u-test.html |
| 2324 critical = maxSize < 5 || minSize < 3 ? 0 : uTable[maxSize][minSize - 3]; |
| 2325 return u <= critical ? (u == u1 ? 1 : -1) : 0; |
| 2326 } |
| 2327 |
| 2328 /** |
| 2329 * Reset properties and abort if running. |
| 2330 * |
| 2331 * @memberOf Benchmark |
| 2332 * @returns {Object} The benchmark instance. |
| 2333 */ |
| 2334 function reset() { |
| 2335 var data, |
| 2336 event, |
| 2337 me = this, |
| 2338 index = 0, |
| 2339 changes = { 'length': 0 }, |
| 2340 queue = { 'length': 0 }; |
| 2341 |
| 2342 if (me.running && !calledBy.abort) { |
| 2343 // no worries, `reset()` is called within `abort()` |
| 2344 calledBy.reset = true; |
| 2345 me.abort(); |
| 2346 delete calledBy.reset; |
| 2347 } |
| 2348 else { |
| 2349 // a non-recursive solution to check if properties have changed |
| 2350 // http://www.jslab.dk/articles/non.recursive.preorder.traversal.part4 |
| 2351 data = { 'destination': me, 'source': extend({}, me.constructor.prototype,
me.options) }; |
| 2352 do { |
| 2353 forOwn(data.source, function(value, key) { |
| 2354 var changed, |
| 2355 destination = data.destination, |
| 2356 currValue = destination[key]; |
| 2357 |
| 2358 if (value && typeof value == 'object') { |
| 2359 if (isClassOf(value, 'Array')) { |
| 2360 // check if an array value has changed to a non-array value |
| 2361 if (!isClassOf(currValue, 'Array')) { |
| 2362 changed = currValue = []; |
| 2363 } |
| 2364 // or has changed its length |
| 2365 if (currValue.length != value.length) { |
| 2366 changed = currValue = currValue.slice(0, value.length); |
| 2367 currValue.length = value.length; |
| 2368 } |
| 2369 } |
| 2370 // check if an object has changed to a non-object value |
| 2371 else if (!currValue || typeof currValue != 'object') { |
| 2372 changed = currValue = {}; |
| 2373 } |
| 2374 // register a changed object |
| 2375 if (changed) { |
| 2376 changes[changes.length++] = { 'destination': destination, 'key': k
ey, 'value': currValue }; |
| 2377 } |
| 2378 queue[queue.length++] = { 'destination': currValue, 'source': value
}; |
| 2379 } |
| 2380 // register a changed primitive |
| 2381 else if (value !== currValue && !(value == null || isClassOf(value, 'F
unction'))) { |
| 2382 changes[changes.length++] = { 'destination': destination, 'key': key
, 'value': value }; |
| 2383 } |
| 2384 }); |
| 2385 } |
| 2386 while ((data = queue[index++])); |
| 2387 |
| 2388 // if changed emit the `reset` event and if it isn't cancelled reset the b
enchmark |
| 2389 if (changes.length && (me.emit(event = Event('reset')), !event.cancelled))
{ |
| 2390 forEach(changes, function(data) { |
| 2391 data.destination[data.key] = data.value; |
| 2392 }); |
| 2393 } |
| 2394 } |
| 2395 return me; |
| 2396 } |
| 2397 |
| 2398 /** |
| 2399 * Displays relevant benchmark information when coerced to a string. |
| 2400 * |
| 2401 * @name toString |
| 2402 * @memberOf Benchmark |
| 2403 * @returns {String} A string representation of the benchmark instance. |
| 2404 */ |
| 2405 function toStringBench() { |
| 2406 var me = this, |
| 2407 error = me.error, |
| 2408 hz = me.hz, |
| 2409 id = me.id, |
| 2410 stats = me.stats, |
| 2411 size = stats.sample.length, |
| 2412 pm = support.java ? '+/-' : '\xb1', |
| 2413 result = me.name || (isNaN(id) ? id : '<Test #' + id + '>'); |
| 2414 |
| 2415 if (error) { |
| 2416 result += ': ' + join(error); |
| 2417 } else { |
| 2418 result += ' x ' + formatNumber(hz.toFixed(hz < 100 ? 2 : 0)) + ' ops/sec '
+ pm + |
| 2419 stats.rme.toFixed(2) + '% (' + size + ' run' + (size == 1 ? '' : 's') +
' sampled)'; |
| 2420 } |
| 2421 return result; |
| 2422 } |
| 2423 |
| 2424 /*--------------------------------------------------------------------------*/ |
| 2425 |
| 2426 /** |
| 2427 * Clocks the time taken to execute a test per cycle (secs). |
| 2428 * |
| 2429 * @private |
| 2430 * @param {Object} bench The benchmark instance. |
| 2431 * @returns {Number} The time taken. |
| 2432 */ |
| 2433 function clock() { |
| 2434 var applet, |
| 2435 options = Benchmark.options, |
| 2436 template = { 'begin': 's$=new n$', 'end': 'r$=(new n$-s$)/1e3', 'uid': u
id }, |
| 2437 timers = [{ 'ns': timer.ns, 'res': max(0.0015, getRes('ms')), 'unit': 'm
s' }]; |
| 2438 |
| 2439 // lazy define for hi-res timers |
| 2440 clock = function(clone) { |
| 2441 var deferred; |
| 2442 if (clone instanceof Deferred) { |
| 2443 deferred = clone; |
| 2444 clone = deferred.benchmark; |
| 2445 } |
| 2446 |
| 2447 var bench = clone._original, |
| 2448 fn = bench.fn, |
| 2449 fnArg = deferred ? getFirstArgument(fn) || 'deferred' : '', |
| 2450 stringable = isStringable(fn); |
| 2451 |
| 2452 var source = { |
| 2453 'setup': getSource(bench.setup, preprocess('m$.setup()')), |
| 2454 'fn': getSource(fn, preprocess('m$.fn(' + fnArg + ')')), |
| 2455 'fnArg': fnArg, |
| 2456 'teardown': getSource(bench.teardown, preprocess('m$.teardown()')) |
| 2457 }; |
| 2458 |
| 2459 var count = bench.count = clone.count, |
| 2460 decompilable = support.decompilation || stringable, |
| 2461 id = bench.id, |
| 2462 isEmpty = !(source.fn || stringable), |
| 2463 name = bench.name || (typeof id == 'number' ? '<Test #' + id + '>' : i
d), |
| 2464 ns = timer.ns, |
| 2465 result = 0; |
| 2466 |
| 2467 // init `minTime` if needed |
| 2468 clone.minTime = bench.minTime || (bench.minTime = bench.options.minTime =
options.minTime); |
| 2469 |
| 2470 // repair nanosecond timer |
| 2471 // (some Chrome builds erase the `ns` variable after millions of execution
s) |
| 2472 if (applet) { |
| 2473 try { |
| 2474 ns.nanoTime(); |
| 2475 } catch(e) { |
| 2476 // use non-element to avoid issues with libs that augment them |
| 2477 ns = timer.ns = new applet.Packages.nano; |
| 2478 } |
| 2479 } |
| 2480 |
| 2481 // Compile in setup/teardown functions and the test loop. |
| 2482 // Create a new compiled test, instead of using the cached `bench.compiled
`, |
| 2483 // to avoid potential engine optimizations enabled over the life of the te
st. |
| 2484 var compiled = bench.compiled = createFunction(preprocess('t$'), interpola
te( |
| 2485 preprocess(deferred |
| 2486 ? 'var d$=this,#{fnArg}=d$,m$=d$.benchmark._original,f$=m$.fn,su$=m$.s
etup,td$=m$.teardown;' + |
| 2487 // when `deferred.cycles` is `0` then... |
| 2488 'if(!d$.cycles){' + |
| 2489 // set `deferred.fn` |
| 2490 'd$.fn=function(){var #{fnArg}=d$;if(typeof f$=="function"){try{#{fn
}\n}catch(e$){f$(d$)}}else{#{fn}\n}};' + |
| 2491 // set `deferred.teardown` |
| 2492 'd$.teardown=function(){d$.cycles=0;if(typeof td$=="function"){try{#
{teardown}\n}catch(e$){td$()}}else{#{teardown}\n}};' + |
| 2493 // execute the benchmark's `setup` |
| 2494 'if(typeof su$=="function"){try{#{setup}\n}catch(e$){su$()}}else{#{s
etup}\n};' + |
| 2495 // start timer |
| 2496 't$.start(d$);' + |
| 2497 // execute `deferred.fn` and return a dummy object |
| 2498 '}d$.fn();return{}' |
| 2499 |
| 2500 : 'var r$,s$,m$=this,f$=m$.fn,i$=m$.count,n$=t$.ns;#{setup}\n#{begin};
' + |
| 2501 'while(i$--){#{fn}\n}#{end};#{teardown}\nreturn{elapsed:r$,uid:"#{ui
d}"}'), |
| 2502 source |
| 2503 )); |
| 2504 |
| 2505 try { |
| 2506 if (isEmpty) { |
| 2507 // Firefox may remove dead code from Function#toString results |
| 2508 // http://bugzil.la/536085 |
| 2509 throw new Error('The test "' + name + '" is empty. This may be the res
ult of dead code removal.'); |
| 2510 } |
| 2511 else if (!deferred) { |
| 2512 // pretest to determine if compiled code is exits early, usually by a |
| 2513 // rogue `return` statement, by checking for a return object with the
uid |
| 2514 bench.count = 1; |
| 2515 compiled = (compiled.call(bench, timer) || {}).uid == uid && compiled; |
| 2516 bench.count = count; |
| 2517 } |
| 2518 } catch(e) { |
| 2519 compiled = null; |
| 2520 clone.error = e || new Error(String(e)); |
| 2521 bench.count = count; |
| 2522 } |
| 2523 // fallback when a test exits early or errors during pretest |
| 2524 if (decompilable && !compiled && !deferred && !isEmpty) { |
| 2525 compiled = createFunction(preprocess('t$'), interpolate( |
| 2526 preprocess( |
| 2527 (clone.error && !stringable |
| 2528 ? 'var r$,s$,m$=this,f$=m$.fn,i$=m$.count' |
| 2529 : 'function f$(){#{fn}\n}var r$,s$,m$=this,i$=m$.count' |
| 2530 ) + |
| 2531 ',n$=t$.ns;#{setup}\n#{begin};m$.f$=f$;while(i$--){m$.f$()}#{end};'
+ |
| 2532 'delete m$.f$;#{teardown}\nreturn{elapsed:r$}' |
| 2533 ), |
| 2534 source |
| 2535 )); |
| 2536 |
| 2537 try { |
| 2538 // pretest one more time to check for errors |
| 2539 bench.count = 1; |
| 2540 compiled.call(bench, timer); |
| 2541 bench.compiled = compiled; |
| 2542 bench.count = count; |
| 2543 delete clone.error; |
| 2544 } |
| 2545 catch(e) { |
| 2546 bench.count = count; |
| 2547 if (clone.error) { |
| 2548 compiled = null; |
| 2549 } else { |
| 2550 bench.compiled = compiled; |
| 2551 clone.error = e || new Error(String(e)); |
| 2552 } |
| 2553 } |
| 2554 } |
| 2555 // assign `compiled` to `clone` before calling in case a deferred benchmar
k |
| 2556 // immediately calls `deferred.resolve()` |
| 2557 clone.compiled = compiled; |
| 2558 // if no errors run the full test loop |
| 2559 if (!clone.error) { |
| 2560 result = compiled.call(deferred || bench, timer).elapsed; |
| 2561 } |
| 2562 return result; |
| 2563 }; |
| 2564 |
| 2565 /*------------------------------------------------------------------------*/ |
| 2566 |
| 2567 /** |
| 2568 * Gets the current timer's minimum resolution (secs). |
| 2569 */ |
| 2570 function getRes(unit) { |
| 2571 var measured, |
| 2572 begin, |
| 2573 count = 30, |
| 2574 divisor = 1e3, |
| 2575 ns = timer.ns, |
| 2576 sample = []; |
| 2577 |
| 2578 // get average smallest measurable time |
| 2579 while (count--) { |
| 2580 if (unit == 'us') { |
| 2581 divisor = 1e6; |
| 2582 if (ns.stop) { |
| 2583 ns.start(); |
| 2584 while (!(measured = ns.microseconds())) { } |
| 2585 } else if (ns[perfName]) { |
| 2586 divisor = 1e3; |
| 2587 measured = Function('n', 'var r,s=n.' + perfName + '();while(!(r=n.'
+ perfName + '()-s)){};return r')(ns); |
| 2588 } else { |
| 2589 begin = ns(); |
| 2590 while (!(measured = ns() - begin)) { } |
| 2591 } |
| 2592 } |
| 2593 else if (unit == 'ns') { |
| 2594 divisor = 1e9; |
| 2595 if (ns.nanoTime) { |
| 2596 begin = ns.nanoTime(); |
| 2597 while (!(measured = ns.nanoTime() - begin)) { } |
| 2598 } else { |
| 2599 begin = (begin = ns())[0] + (begin[1] / divisor); |
| 2600 while (!(measured = ((measured = ns())[0] + (measured[1] / divisor))
- begin)) { } |
| 2601 divisor = 1; |
| 2602 } |
| 2603 } |
| 2604 else { |
| 2605 begin = new ns; |
| 2606 while (!(measured = new ns - begin)) { } |
| 2607 } |
| 2608 // check for broken timers (nanoTime may have issues) |
| 2609 // http://alivebutsleepy.srnet.cz/unreliable-system-nanotime/ |
| 2610 if (measured > 0) { |
| 2611 sample.push(measured); |
| 2612 } else { |
| 2613 sample.push(Infinity); |
| 2614 break; |
| 2615 } |
| 2616 } |
| 2617 // convert to seconds |
| 2618 return getMean(sample) / divisor; |
| 2619 } |
| 2620 |
| 2621 /** |
| 2622 * Replaces all occurrences of `$` with a unique number and |
| 2623 * template tokens with content. |
| 2624 */ |
| 2625 function preprocess(code) { |
| 2626 return interpolate(code, template).replace(/\$/g, /\d+/.exec(uid)); |
| 2627 } |
| 2628 |
| 2629 /*------------------------------------------------------------------------*/ |
| 2630 |
| 2631 // detect nanosecond support from a Java applet |
| 2632 each(doc && doc.applets || [], function(element) { |
| 2633 return !(timer.ns = applet = 'nanoTime' in element && element); |
| 2634 }); |
| 2635 |
| 2636 // check type in case Safari returns an object instead of a number |
| 2637 try { |
| 2638 if (typeof timer.ns.nanoTime() == 'number') { |
| 2639 timers.push({ 'ns': timer.ns, 'res': getRes('ns'), 'unit': 'ns' }); |
| 2640 } |
| 2641 } catch(e) { } |
| 2642 |
| 2643 // detect Chrome's microsecond timer: |
| 2644 // enable benchmarking via the --enable-benchmarking command |
| 2645 // line switch in at least Chrome 7 to use chrome.Interval |
| 2646 try { |
| 2647 if ((timer.ns = new (window.chrome || window.chromium).Interval)) { |
| 2648 timers.push({ 'ns': timer.ns, 'res': getRes('us'), 'unit': 'us' }); |
| 2649 } |
| 2650 } catch(e) { } |
| 2651 |
| 2652 // detect `performance.now` microsecond resolution timer |
| 2653 if ((timer.ns = perfName && perfObject)) { |
| 2654 timers.push({ 'ns': timer.ns, 'res': getRes('us'), 'unit': 'us' }); |
| 2655 } |
| 2656 |
| 2657 // detect Node's nanosecond resolution timer available in Node >= 0.8 |
| 2658 if (processObject && typeof (timer.ns = processObject.hrtime) == 'function')
{ |
| 2659 timers.push({ 'ns': timer.ns, 'res': getRes('ns'), 'unit': 'ns' }); |
| 2660 } |
| 2661 |
| 2662 // detect Wade Simmons' Node microtime module |
| 2663 if (microtimeObject && typeof (timer.ns = microtimeObject.now) == 'function'
) { |
| 2664 timers.push({ 'ns': timer.ns, 'res': getRes('us'), 'unit': 'us' }); |
| 2665 } |
| 2666 |
| 2667 // pick timer with highest resolution |
| 2668 timer = reduce(timers, function(timer, other) { |
| 2669 return other.res < timer.res ? other : timer; |
| 2670 }); |
| 2671 |
| 2672 // remove unused applet |
| 2673 if (timer.unit != 'ns' && applet) { |
| 2674 applet = destroyElement(applet); |
| 2675 } |
| 2676 // error if there are no working timers |
| 2677 if (timer.res == Infinity) { |
| 2678 throw new Error('Benchmark.js was unable to find a working timer.'); |
| 2679 } |
| 2680 // use API of chosen timer |
| 2681 if (timer.unit == 'ns') { |
| 2682 if (timer.ns.nanoTime) { |
| 2683 extend(template, { |
| 2684 'begin': 's$=n$.nanoTime()', |
| 2685 'end': 'r$=(n$.nanoTime()-s$)/1e9' |
| 2686 }); |
| 2687 } else { |
| 2688 extend(template, { |
| 2689 'begin': 's$=n$()', |
| 2690 'end': 'r$=n$(s$);r$=r$[0]+(r$[1]/1e9)' |
| 2691 }); |
| 2692 } |
| 2693 } |
| 2694 else if (timer.unit == 'us') { |
| 2695 if (timer.ns.stop) { |
| 2696 extend(template, { |
| 2697 'begin': 's$=n$.start()', |
| 2698 'end': 'r$=n$.microseconds()/1e6' |
| 2699 }); |
| 2700 } else if (perfName) { |
| 2701 extend(template, { |
| 2702 'begin': 's$=n$.' + perfName + '()', |
| 2703 'end': 'r$=(n$.' + perfName + '()-s$)/1e3' |
| 2704 }); |
| 2705 } else { |
| 2706 extend(template, { |
| 2707 'begin': 's$=n$()', |
| 2708 'end': 'r$=(n$()-s$)/1e6' |
| 2709 }); |
| 2710 } |
| 2711 } |
| 2712 |
| 2713 // define `timer` methods |
| 2714 timer.start = createFunction(preprocess('o$'), |
| 2715 preprocess('var n$=this.ns,#{begin};o$.elapsed=0;o$.timeStamp=s$')); |
| 2716 |
| 2717 timer.stop = createFunction(preprocess('o$'), |
| 2718 preprocess('var n$=this.ns,s$=o$.timeStamp,#{end};o$.elapsed=r$')); |
| 2719 |
| 2720 // resolve time span required to achieve a percent uncertainty of at most 1% |
| 2721 // http://spiff.rit.edu/classes/phys273/uncert/uncert.html |
| 2722 options.minTime || (options.minTime = max(timer.res / 2 / 0.01, 0.05)); |
| 2723 return clock.apply(null, arguments); |
| 2724 } |
| 2725 |
| 2726 /*--------------------------------------------------------------------------*/ |
| 2727 |
| 2728 /** |
| 2729 * Computes stats on benchmark results. |
| 2730 * |
| 2731 * @private |
| 2732 * @param {Object} bench The benchmark instance. |
| 2733 * @param {Object} options The options object. |
| 2734 */ |
| 2735 function compute(bench, options) { |
| 2736 options || (options = {}); |
| 2737 |
| 2738 var async = options.async, |
| 2739 elapsed = 0, |
| 2740 initCount = bench.initCount, |
| 2741 minSamples = bench.minSamples, |
| 2742 queue = [], |
| 2743 sample = bench.stats.sample; |
| 2744 |
| 2745 /** |
| 2746 * Adds a clone to the queue. |
| 2747 */ |
| 2748 function enqueue() { |
| 2749 queue.push(bench.clone({ |
| 2750 '_original': bench, |
| 2751 'events': { |
| 2752 'abort': [update], |
| 2753 'cycle': [update], |
| 2754 'error': [update], |
| 2755 'start': [update] |
| 2756 } |
| 2757 })); |
| 2758 } |
| 2759 |
| 2760 /** |
| 2761 * Updates the clone/original benchmarks to keep their data in sync. |
| 2762 */ |
| 2763 function update(event) { |
| 2764 var clone = this, |
| 2765 type = event.type; |
| 2766 |
| 2767 if (bench.running) { |
| 2768 if (type == 'start') { |
| 2769 // Note: `clone.minTime` prop is inited in `clock()` |
| 2770 clone.count = bench.initCount; |
| 2771 } |
| 2772 else { |
| 2773 if (type == 'error') { |
| 2774 bench.error = clone.error; |
| 2775 } |
| 2776 if (type == 'abort') { |
| 2777 bench.abort(); |
| 2778 bench.emit('cycle'); |
| 2779 } else { |
| 2780 event.currentTarget = event.target = bench; |
| 2781 bench.emit(event); |
| 2782 } |
| 2783 } |
| 2784 } else if (bench.aborted) { |
| 2785 // clear abort listeners to avoid triggering bench's abort/cycle again |
| 2786 clone.events.abort.length = 0; |
| 2787 clone.abort(); |
| 2788 } |
| 2789 } |
| 2790 |
| 2791 /** |
| 2792 * Determines if more clones should be queued or if cycling should stop. |
| 2793 */ |
| 2794 function evaluate(event) { |
| 2795 var critical, |
| 2796 df, |
| 2797 mean, |
| 2798 moe, |
| 2799 rme, |
| 2800 sd, |
| 2801 sem, |
| 2802 variance, |
| 2803 clone = event.target, |
| 2804 done = bench.aborted, |
| 2805 now = +new Date, |
| 2806 size = sample.push(clone.times.period), |
| 2807 maxedOut = size >= minSamples && (elapsed += now - clone.times.timeSta
mp) / 1e3 > bench.maxTime, |
| 2808 times = bench.times, |
| 2809 varOf = function(sum, x) { return sum + pow(x - mean, 2); }; |
| 2810 |
| 2811 // exit early for aborted or unclockable tests |
| 2812 if (done || clone.hz == Infinity) { |
| 2813 maxedOut = !(size = sample.length = queue.length = 0); |
| 2814 } |
| 2815 |
| 2816 if (!done) { |
| 2817 // sample mean (estimate of the population mean) |
| 2818 mean = getMean(sample); |
| 2819 // sample variance (estimate of the population variance) |
| 2820 variance = reduce(sample, varOf, 0) / (size - 1) || 0; |
| 2821 // sample standard deviation (estimate of the population standard deviat
ion) |
| 2822 sd = sqrt(variance); |
| 2823 // standard error of the mean (a.k.a. the standard deviation of the samp
ling distribution of the sample mean) |
| 2824 sem = sd / sqrt(size); |
| 2825 // degrees of freedom |
| 2826 df = size - 1; |
| 2827 // critical value |
| 2828 critical = tTable[Math.round(df) || 1] || tTable.infinity; |
| 2829 // margin of error |
| 2830 moe = sem * critical; |
| 2831 // relative margin of error |
| 2832 rme = (moe / mean) * 100 || 0; |
| 2833 |
| 2834 extend(bench.stats, { |
| 2835 'deviation': sd, |
| 2836 'mean': mean, |
| 2837 'moe': moe, |
| 2838 'rme': rme, |
| 2839 'sem': sem, |
| 2840 'variance': variance |
| 2841 }); |
| 2842 |
| 2843 // Abort the cycle loop when the minimum sample size has been collected |
| 2844 // and the elapsed time exceeds the maximum time allowed per benchmark. |
| 2845 // We don't count cycle delays toward the max time because delays may be |
| 2846 // increased by browsers that clamp timeouts for inactive tabs. |
| 2847 // https://developer.mozilla.org/en/window.setTimeout#Inactive_tabs |
| 2848 if (maxedOut) { |
| 2849 // reset the `initCount` in case the benchmark is rerun |
| 2850 bench.initCount = initCount; |
| 2851 bench.running = false; |
| 2852 done = true; |
| 2853 times.elapsed = (now - times.timeStamp) / 1e3; |
| 2854 } |
| 2855 if (bench.hz != Infinity) { |
| 2856 bench.hz = 1 / mean; |
| 2857 times.cycle = mean * bench.count; |
| 2858 times.period = mean; |
| 2859 } |
| 2860 } |
| 2861 // if time permits, increase sample size to reduce the margin of error |
| 2862 if (queue.length < 2 && !maxedOut) { |
| 2863 enqueue(); |
| 2864 } |
| 2865 // abort the invoke cycle when done |
| 2866 event.aborted = done; |
| 2867 } |
| 2868 |
| 2869 // init queue and begin |
| 2870 enqueue(); |
| 2871 invoke(queue, { |
| 2872 'name': 'run', |
| 2873 'args': { 'async': async }, |
| 2874 'queued': true, |
| 2875 'onCycle': evaluate, |
| 2876 'onComplete': function() { bench.emit('complete'); } |
| 2877 }); |
| 2878 } |
| 2879 |
| 2880 /*--------------------------------------------------------------------------*/ |
| 2881 |
| 2882 /** |
| 2883 * Cycles a benchmark until a run `count` can be established. |
| 2884 * |
| 2885 * @private |
| 2886 * @param {Object} clone The cloned benchmark instance. |
| 2887 * @param {Object} options The options object. |
| 2888 */ |
| 2889 function cycle(clone, options) { |
| 2890 options || (options = {}); |
| 2891 |
| 2892 var deferred; |
| 2893 if (clone instanceof Deferred) { |
| 2894 deferred = clone; |
| 2895 clone = clone.benchmark; |
| 2896 } |
| 2897 |
| 2898 var clocked, |
| 2899 cycles, |
| 2900 divisor, |
| 2901 event, |
| 2902 minTime, |
| 2903 period, |
| 2904 async = options.async, |
| 2905 bench = clone._original, |
| 2906 count = clone.count, |
| 2907 times = clone.times; |
| 2908 |
| 2909 // continue, if not aborted between cycles |
| 2910 if (clone.running) { |
| 2911 // `minTime` is set to `Benchmark.options.minTime` in `clock()` |
| 2912 cycles = ++clone.cycles; |
| 2913 clocked = deferred ? deferred.elapsed : clock(clone); |
| 2914 minTime = clone.minTime; |
| 2915 |
| 2916 if (cycles > bench.cycles) { |
| 2917 bench.cycles = cycles; |
| 2918 } |
| 2919 if (clone.error) { |
| 2920 event = Event('error'); |
| 2921 event.message = clone.error; |
| 2922 clone.emit(event); |
| 2923 if (!event.cancelled) { |
| 2924 clone.abort(); |
| 2925 } |
| 2926 } |
| 2927 } |
| 2928 |
| 2929 // continue, if not errored |
| 2930 if (clone.running) { |
| 2931 // time taken to complete last test cycle |
| 2932 bench.times.cycle = times.cycle = clocked; |
| 2933 // seconds per operation |
| 2934 period = bench.times.period = times.period = clocked / count; |
| 2935 // ops per second |
| 2936 bench.hz = clone.hz = 1 / period; |
| 2937 // avoid working our way up to this next time |
| 2938 bench.initCount = clone.initCount = count; |
| 2939 // do we need to do another cycle? |
| 2940 clone.running = clocked < minTime; |
| 2941 |
| 2942 if (clone.running) { |
| 2943 // tests may clock at `0` when `initCount` is a small number, |
| 2944 // to avoid that we set its count to something a bit higher |
| 2945 if (!clocked && (divisor = divisors[clone.cycles]) != null) { |
| 2946 count = floor(4e6 / divisor); |
| 2947 } |
| 2948 // calculate how many more iterations it will take to achive the `minTim
e` |
| 2949 if (count <= clone.count) { |
| 2950 count += Math.ceil((minTime - clocked) / period); |
| 2951 } |
| 2952 clone.running = count != Infinity; |
| 2953 } |
| 2954 } |
| 2955 // should we exit early? |
| 2956 event = Event('cycle'); |
| 2957 clone.emit(event); |
| 2958 if (event.aborted) { |
| 2959 clone.abort(); |
| 2960 } |
| 2961 // figure out what to do next |
| 2962 if (clone.running) { |
| 2963 // start a new cycle |
| 2964 clone.count = count; |
| 2965 if (deferred) { |
| 2966 clone.compiled.call(deferred, timer); |
| 2967 } else if (async) { |
| 2968 delay(clone, function() { cycle(clone, options); }); |
| 2969 } else { |
| 2970 cycle(clone); |
| 2971 } |
| 2972 } |
| 2973 else { |
| 2974 // fix TraceMonkey bug associated with clock fallbacks |
| 2975 // http://bugzil.la/509069 |
| 2976 if (support.browser) { |
| 2977 runScript(uid + '=1;delete ' + uid); |
| 2978 } |
| 2979 // done |
| 2980 clone.emit('complete'); |
| 2981 } |
| 2982 } |
| 2983 |
| 2984 /*--------------------------------------------------------------------------*/ |
| 2985 |
| 2986 /** |
| 2987 * Runs the benchmark. |
| 2988 * |
| 2989 * @memberOf Benchmark |
| 2990 * @param {Object} [options={}] Options object. |
| 2991 * @returns {Object} The benchmark instance. |
| 2992 * @example |
| 2993 * |
| 2994 * // basic usage |
| 2995 * bench.run(); |
| 2996 * |
| 2997 * // or with options |
| 2998 * bench.run({ 'async': true }); |
| 2999 */ |
| 3000 function run(options) { |
| 3001 var me = this, |
| 3002 event = Event('start'); |
| 3003 |
| 3004 // set `running` to `false` so `reset()` won't call `abort()` |
| 3005 me.running = false; |
| 3006 me.reset(); |
| 3007 me.running = true; |
| 3008 |
| 3009 me.count = me.initCount; |
| 3010 me.times.timeStamp = +new Date; |
| 3011 me.emit(event); |
| 3012 |
| 3013 if (!event.cancelled) { |
| 3014 options = { 'async': ((options = options && options.async) == null ? me.as
ync : options) && support.timeout }; |
| 3015 |
| 3016 // for clones created within `compute()` |
| 3017 if (me._original) { |
| 3018 if (me.defer) { |
| 3019 Deferred(me); |
| 3020 } else { |
| 3021 cycle(me, options); |
| 3022 } |
| 3023 } |
| 3024 // for original benchmarks |
| 3025 else { |
| 3026 compute(me, options); |
| 3027 } |
| 3028 } |
| 3029 return me; |
| 3030 } |
| 3031 |
| 3032 /*--------------------------------------------------------------------------*/ |
| 3033 |
| 3034 // Firefox 1 erroneously defines variable and argument names of functions on |
| 3035 // the function itself as non-configurable properties with `undefined` values. |
| 3036 // The bugginess continues as the `Benchmark` constructor has an argument |
| 3037 // named `options` and Firefox 1 will not assign a value to `Benchmark.options
`, |
| 3038 // making it non-writable in the process, unless it is the first property |
| 3039 // assigned by for-in loop of `extend()`. |
| 3040 extend(Benchmark, { |
| 3041 |
| 3042 /** |
| 3043 * The default options copied by benchmark instances. |
| 3044 * |
| 3045 * @static |
| 3046 * @memberOf Benchmark |
| 3047 * @type Object |
| 3048 */ |
| 3049 'options': { |
| 3050 |
| 3051 /** |
| 3052 * A flag to indicate that benchmark cycles will execute asynchronously |
| 3053 * by default. |
| 3054 * |
| 3055 * @memberOf Benchmark.options |
| 3056 * @type Boolean |
| 3057 */ |
| 3058 'async': false, |
| 3059 |
| 3060 /** |
| 3061 * A flag to indicate that the benchmark clock is deferred. |
| 3062 * |
| 3063 * @memberOf Benchmark.options |
| 3064 * @type Boolean |
| 3065 */ |
| 3066 'defer': false, |
| 3067 |
| 3068 /** |
| 3069 * The delay between test cycles (secs). |
| 3070 * @memberOf Benchmark.options |
| 3071 * @type Number |
| 3072 */ |
| 3073 'delay': 0.005, |
| 3074 |
| 3075 /** |
| 3076 * Displayed by Benchmark#toString when a `name` is not available |
| 3077 * (auto-generated if absent). |
| 3078 * |
| 3079 * @memberOf Benchmark.options |
| 3080 * @type String |
| 3081 */ |
| 3082 'id': undefined, |
| 3083 |
| 3084 /** |
| 3085 * The default number of times to execute a test on a benchmark's first cy
cle. |
| 3086 * |
| 3087 * @memberOf Benchmark.options |
| 3088 * @type Number |
| 3089 */ |
| 3090 'initCount': 1, |
| 3091 |
| 3092 /** |
| 3093 * The maximum time a benchmark is allowed to run before finishing (secs). |
| 3094 * Note: Cycle delays aren't counted toward the maximum time. |
| 3095 * |
| 3096 * @memberOf Benchmark.options |
| 3097 * @type Number |
| 3098 */ |
| 3099 'maxTime': 5, |
| 3100 |
| 3101 /** |
| 3102 * The minimum sample size required to perform statistical analysis. |
| 3103 * |
| 3104 * @memberOf Benchmark.options |
| 3105 * @type Number |
| 3106 */ |
| 3107 'minSamples': 5, |
| 3108 |
| 3109 /** |
| 3110 * The time needed to reduce the percent uncertainty of measurement to 1%
(secs). |
| 3111 * |
| 3112 * @memberOf Benchmark.options |
| 3113 * @type Number |
| 3114 */ |
| 3115 'minTime': 0, |
| 3116 |
| 3117 /** |
| 3118 * The name of the benchmark. |
| 3119 * |
| 3120 * @memberOf Benchmark.options |
| 3121 * @type String |
| 3122 */ |
| 3123 'name': undefined, |
| 3124 |
| 3125 /** |
| 3126 * An event listener called when the benchmark is aborted. |
| 3127 * |
| 3128 * @memberOf Benchmark.options |
| 3129 * @type Function |
| 3130 */ |
| 3131 'onAbort': undefined, |
| 3132 |
| 3133 /** |
| 3134 * An event listener called when the benchmark completes running. |
| 3135 * |
| 3136 * @memberOf Benchmark.options |
| 3137 * @type Function |
| 3138 */ |
| 3139 'onComplete': undefined, |
| 3140 |
| 3141 /** |
| 3142 * An event listener called after each run cycle. |
| 3143 * |
| 3144 * @memberOf Benchmark.options |
| 3145 * @type Function |
| 3146 */ |
| 3147 'onCycle': undefined, |
| 3148 |
| 3149 /** |
| 3150 * An event listener called when a test errors. |
| 3151 * |
| 3152 * @memberOf Benchmark.options |
| 3153 * @type Function |
| 3154 */ |
| 3155 'onError': undefined, |
| 3156 |
| 3157 /** |
| 3158 * An event listener called when the benchmark is reset. |
| 3159 * |
| 3160 * @memberOf Benchmark.options |
| 3161 * @type Function |
| 3162 */ |
| 3163 'onReset': undefined, |
| 3164 |
| 3165 /** |
| 3166 * An event listener called when the benchmark starts running. |
| 3167 * |
| 3168 * @memberOf Benchmark.options |
| 3169 * @type Function |
| 3170 */ |
| 3171 'onStart': undefined |
| 3172 }, |
| 3173 |
| 3174 /** |
| 3175 * Platform object with properties describing things like browser name, |
| 3176 * version, and operating system. |
| 3177 * |
| 3178 * @static |
| 3179 * @memberOf Benchmark |
| 3180 * @type Object |
| 3181 */ |
| 3182 'platform': req('platform') || window.platform || { |
| 3183 |
| 3184 /** |
| 3185 * The platform description. |
| 3186 * |
| 3187 * @memberOf Benchmark.platform |
| 3188 * @type String |
| 3189 */ |
| 3190 'description': window.navigator && navigator.userAgent || null, |
| 3191 |
| 3192 /** |
| 3193 * The name of the browser layout engine. |
| 3194 * |
| 3195 * @memberOf Benchmark.platform |
| 3196 * @type String|Null |
| 3197 */ |
| 3198 'layout': null, |
| 3199 |
| 3200 /** |
| 3201 * The name of the product hosting the browser. |
| 3202 * |
| 3203 * @memberOf Benchmark.platform |
| 3204 * @type String|Null |
| 3205 */ |
| 3206 'product': null, |
| 3207 |
| 3208 /** |
| 3209 * The name of the browser/environment. |
| 3210 * |
| 3211 * @memberOf Benchmark.platform |
| 3212 * @type String|Null |
| 3213 */ |
| 3214 'name': null, |
| 3215 |
| 3216 /** |
| 3217 * The name of the product's manufacturer. |
| 3218 * |
| 3219 * @memberOf Benchmark.platform |
| 3220 * @type String|Null |
| 3221 */ |
| 3222 'manufacturer': null, |
| 3223 |
| 3224 /** |
| 3225 * The name of the operating system. |
| 3226 * |
| 3227 * @memberOf Benchmark.platform |
| 3228 * @type String|Null |
| 3229 */ |
| 3230 'os': null, |
| 3231 |
| 3232 /** |
| 3233 * The alpha/beta release indicator. |
| 3234 * |
| 3235 * @memberOf Benchmark.platform |
| 3236 * @type String|Null |
| 3237 */ |
| 3238 'prerelease': null, |
| 3239 |
| 3240 /** |
| 3241 * The browser/environment version. |
| 3242 * |
| 3243 * @memberOf Benchmark.platform |
| 3244 * @type String|Null |
| 3245 */ |
| 3246 'version': null, |
| 3247 |
| 3248 /** |
| 3249 * Return platform description when the platform object is coerced to a st
ring. |
| 3250 * |
| 3251 * @memberOf Benchmark.platform |
| 3252 * @type Function |
| 3253 * @returns {String} The platform description. |
| 3254 */ |
| 3255 'toString': function() { |
| 3256 return this.description || ''; |
| 3257 } |
| 3258 }, |
| 3259 |
| 3260 /** |
| 3261 * The semantic version number. |
| 3262 * |
| 3263 * @static |
| 3264 * @memberOf Benchmark |
| 3265 * @type String |
| 3266 */ |
| 3267 'version': '1.0.0', |
| 3268 |
| 3269 // an object of environment/feature detection flags |
| 3270 'support': support, |
| 3271 |
| 3272 // clone objects |
| 3273 'deepClone': deepClone, |
| 3274 |
| 3275 // iteration utility |
| 3276 'each': each, |
| 3277 |
| 3278 // augment objects |
| 3279 'extend': extend, |
| 3280 |
| 3281 // generic Array#filter |
| 3282 'filter': filter, |
| 3283 |
| 3284 // generic Array#forEach |
| 3285 'forEach': forEach, |
| 3286 |
| 3287 // generic own property iteration utility |
| 3288 'forOwn': forOwn, |
| 3289 |
| 3290 // converts a number to a comma-separated string |
| 3291 'formatNumber': formatNumber, |
| 3292 |
| 3293 // generic Object#hasOwnProperty |
| 3294 // (trigger hasKey's lazy define before assigning it to Benchmark) |
| 3295 'hasKey': (hasKey(Benchmark, ''), hasKey), |
| 3296 |
| 3297 // generic Array#indexOf |
| 3298 'indexOf': indexOf, |
| 3299 |
| 3300 // template utility |
| 3301 'interpolate': interpolate, |
| 3302 |
| 3303 // invokes a method on each item in an array |
| 3304 'invoke': invoke, |
| 3305 |
| 3306 // generic Array#join for arrays and objects |
| 3307 'join': join, |
| 3308 |
| 3309 // generic Array#map |
| 3310 'map': map, |
| 3311 |
| 3312 // retrieves a property value from each item in an array |
| 3313 'pluck': pluck, |
| 3314 |
| 3315 // generic Array#reduce |
| 3316 'reduce': reduce |
| 3317 }); |
| 3318 |
| 3319 /*--------------------------------------------------------------------------*/ |
| 3320 |
| 3321 extend(Benchmark.prototype, { |
| 3322 |
| 3323 /** |
| 3324 * The number of times a test was executed. |
| 3325 * |
| 3326 * @memberOf Benchmark |
| 3327 * @type Number |
| 3328 */ |
| 3329 'count': 0, |
| 3330 |
| 3331 /** |
| 3332 * The number of cycles performed while benchmarking. |
| 3333 * |
| 3334 * @memberOf Benchmark |
| 3335 * @type Number |
| 3336 */ |
| 3337 'cycles': 0, |
| 3338 |
| 3339 /** |
| 3340 * The number of executions per second. |
| 3341 * |
| 3342 * @memberOf Benchmark |
| 3343 * @type Number |
| 3344 */ |
| 3345 'hz': 0, |
| 3346 |
| 3347 /** |
| 3348 * The compiled test function. |
| 3349 * |
| 3350 * @memberOf Benchmark |
| 3351 * @type Function|String |
| 3352 */ |
| 3353 'compiled': undefined, |
| 3354 |
| 3355 /** |
| 3356 * The error object if the test failed. |
| 3357 * |
| 3358 * @memberOf Benchmark |
| 3359 * @type Object |
| 3360 */ |
| 3361 'error': undefined, |
| 3362 |
| 3363 /** |
| 3364 * The test to benchmark. |
| 3365 * |
| 3366 * @memberOf Benchmark |
| 3367 * @type Function|String |
| 3368 */ |
| 3369 'fn': undefined, |
| 3370 |
| 3371 /** |
| 3372 * A flag to indicate if the benchmark is aborted. |
| 3373 * |
| 3374 * @memberOf Benchmark |
| 3375 * @type Boolean |
| 3376 */ |
| 3377 'aborted': false, |
| 3378 |
| 3379 /** |
| 3380 * A flag to indicate if the benchmark is running. |
| 3381 * |
| 3382 * @memberOf Benchmark |
| 3383 * @type Boolean |
| 3384 */ |
| 3385 'running': false, |
| 3386 |
| 3387 /** |
| 3388 * Compiled into the test and executed immediately **before** the test loop. |
| 3389 * |
| 3390 * @memberOf Benchmark |
| 3391 * @type Function|String |
| 3392 * @example |
| 3393 * |
| 3394 * // basic usage |
| 3395 * var bench = Benchmark({ |
| 3396 * 'setup': function() { |
| 3397 * var c = this.count, |
| 3398 * element = document.getElementById('container'); |
| 3399 * while (c--) { |
| 3400 * element.appendChild(document.createElement('div')); |
| 3401 * } |
| 3402 * }, |
| 3403 * 'fn': function() { |
| 3404 * element.removeChild(element.lastChild); |
| 3405 * } |
| 3406 * }); |
| 3407 * |
| 3408 * // compiles to something like: |
| 3409 * var c = this.count, |
| 3410 * element = document.getElementById('container'); |
| 3411 * while (c--) { |
| 3412 * element.appendChild(document.createElement('div')); |
| 3413 * } |
| 3414 * var start = new Date; |
| 3415 * while (count--) { |
| 3416 * element.removeChild(element.lastChild); |
| 3417 * } |
| 3418 * var end = new Date - start; |
| 3419 * |
| 3420 * // or using strings |
| 3421 * var bench = Benchmark({ |
| 3422 * 'setup': '\ |
| 3423 * var a = 0;\n\ |
| 3424 * (function() {\n\ |
| 3425 * (function() {\n\ |
| 3426 * (function() {', |
| 3427 * 'fn': 'a += 1;', |
| 3428 * 'teardown': '\ |
| 3429 * }())\n\ |
| 3430 * }())\n\ |
| 3431 * }())' |
| 3432 * }); |
| 3433 * |
| 3434 * // compiles to something like: |
| 3435 * var a = 0; |
| 3436 * (function() { |
| 3437 * (function() { |
| 3438 * (function() { |
| 3439 * var start = new Date; |
| 3440 * while (count--) { |
| 3441 * a += 1; |
| 3442 * } |
| 3443 * var end = new Date - start; |
| 3444 * }()) |
| 3445 * }()) |
| 3446 * }()) |
| 3447 */ |
| 3448 'setup': noop, |
| 3449 |
| 3450 /** |
| 3451 * Compiled into the test and executed immediately **after** the test loop. |
| 3452 * |
| 3453 * @memberOf Benchmark |
| 3454 * @type Function|String |
| 3455 */ |
| 3456 'teardown': noop, |
| 3457 |
| 3458 /** |
| 3459 * An object of stats including mean, margin or error, and standard deviatio
n. |
| 3460 * |
| 3461 * @memberOf Benchmark |
| 3462 * @type Object |
| 3463 */ |
| 3464 'stats': { |
| 3465 |
| 3466 /** |
| 3467 * The margin of error. |
| 3468 * |
| 3469 * @memberOf Benchmark#stats |
| 3470 * @type Number |
| 3471 */ |
| 3472 'moe': 0, |
| 3473 |
| 3474 /** |
| 3475 * The relative margin of error (expressed as a percentage of the mean). |
| 3476 * |
| 3477 * @memberOf Benchmark#stats |
| 3478 * @type Number |
| 3479 */ |
| 3480 'rme': 0, |
| 3481 |
| 3482 /** |
| 3483 * The standard error of the mean. |
| 3484 * |
| 3485 * @memberOf Benchmark#stats |
| 3486 * @type Number |
| 3487 */ |
| 3488 'sem': 0, |
| 3489 |
| 3490 /** |
| 3491 * The sample standard deviation. |
| 3492 * |
| 3493 * @memberOf Benchmark#stats |
| 3494 * @type Number |
| 3495 */ |
| 3496 'deviation': 0, |
| 3497 |
| 3498 /** |
| 3499 * The sample arithmetic mean. |
| 3500 * |
| 3501 * @memberOf Benchmark#stats |
| 3502 * @type Number |
| 3503 */ |
| 3504 'mean': 0, |
| 3505 |
| 3506 /** |
| 3507 * The array of sampled periods. |
| 3508 * |
| 3509 * @memberOf Benchmark#stats |
| 3510 * @type Array |
| 3511 */ |
| 3512 'sample': [], |
| 3513 |
| 3514 /** |
| 3515 * The sample variance. |
| 3516 * |
| 3517 * @memberOf Benchmark#stats |
| 3518 * @type Number |
| 3519 */ |
| 3520 'variance': 0 |
| 3521 }, |
| 3522 |
| 3523 /** |
| 3524 * An object of timing data including cycle, elapsed, period, start, and sto
p. |
| 3525 * |
| 3526 * @memberOf Benchmark |
| 3527 * @type Object |
| 3528 */ |
| 3529 'times': { |
| 3530 |
| 3531 /** |
| 3532 * The time taken to complete the last cycle (secs). |
| 3533 * |
| 3534 * @memberOf Benchmark#times |
| 3535 * @type Number |
| 3536 */ |
| 3537 'cycle': 0, |
| 3538 |
| 3539 /** |
| 3540 * The time taken to complete the benchmark (secs). |
| 3541 * |
| 3542 * @memberOf Benchmark#times |
| 3543 * @type Number |
| 3544 */ |
| 3545 'elapsed': 0, |
| 3546 |
| 3547 /** |
| 3548 * The time taken to execute the test once (secs). |
| 3549 * |
| 3550 * @memberOf Benchmark#times |
| 3551 * @type Number |
| 3552 */ |
| 3553 'period': 0, |
| 3554 |
| 3555 /** |
| 3556 * A timestamp of when the benchmark started (ms). |
| 3557 * |
| 3558 * @memberOf Benchmark#times |
| 3559 * @type Number |
| 3560 */ |
| 3561 'timeStamp': 0 |
| 3562 }, |
| 3563 |
| 3564 // aborts benchmark (does not record times) |
| 3565 'abort': abort, |
| 3566 |
| 3567 // creates a new benchmark using the same test and options |
| 3568 'clone': clone, |
| 3569 |
| 3570 // compares benchmark's hertz with another |
| 3571 'compare': compare, |
| 3572 |
| 3573 // executes listeners |
| 3574 'emit': emit, |
| 3575 |
| 3576 // get listeners |
| 3577 'listeners': listeners, |
| 3578 |
| 3579 // unregister listeners |
| 3580 'off': off, |
| 3581 |
| 3582 // register listeners |
| 3583 'on': on, |
| 3584 |
| 3585 // reset benchmark properties |
| 3586 'reset': reset, |
| 3587 |
| 3588 // runs the benchmark |
| 3589 'run': run, |
| 3590 |
| 3591 // pretty print benchmark info |
| 3592 'toString': toStringBench |
| 3593 }); |
| 3594 |
| 3595 /*--------------------------------------------------------------------------*/ |
| 3596 |
| 3597 extend(Deferred.prototype, { |
| 3598 |
| 3599 /** |
| 3600 * The deferred benchmark instance. |
| 3601 * |
| 3602 * @memberOf Benchmark.Deferred |
| 3603 * @type Object |
| 3604 */ |
| 3605 'benchmark': null, |
| 3606 |
| 3607 /** |
| 3608 * The number of deferred cycles performed while benchmarking. |
| 3609 * |
| 3610 * @memberOf Benchmark.Deferred |
| 3611 * @type Number |
| 3612 */ |
| 3613 'cycles': 0, |
| 3614 |
| 3615 /** |
| 3616 * The time taken to complete the deferred benchmark (secs). |
| 3617 * |
| 3618 * @memberOf Benchmark.Deferred |
| 3619 * @type Number |
| 3620 */ |
| 3621 'elapsed': 0, |
| 3622 |
| 3623 /** |
| 3624 * A timestamp of when the deferred benchmark started (ms). |
| 3625 * |
| 3626 * @memberOf Benchmark.Deferred |
| 3627 * @type Number |
| 3628 */ |
| 3629 'timeStamp': 0, |
| 3630 |
| 3631 // cycles/completes the deferred benchmark |
| 3632 'resolve': resolve |
| 3633 }); |
| 3634 |
| 3635 /*--------------------------------------------------------------------------*/ |
| 3636 |
| 3637 extend(Event.prototype, { |
| 3638 |
| 3639 /** |
| 3640 * A flag to indicate if the emitters listener iteration is aborted. |
| 3641 * |
| 3642 * @memberOf Benchmark.Event |
| 3643 * @type Boolean |
| 3644 */ |
| 3645 'aborted': false, |
| 3646 |
| 3647 /** |
| 3648 * A flag to indicate if the default action is cancelled. |
| 3649 * |
| 3650 * @memberOf Benchmark.Event |
| 3651 * @type Boolean |
| 3652 */ |
| 3653 'cancelled': false, |
| 3654 |
| 3655 /** |
| 3656 * The object whose listeners are currently being processed. |
| 3657 * |
| 3658 * @memberOf Benchmark.Event |
| 3659 * @type Object |
| 3660 */ |
| 3661 'currentTarget': undefined, |
| 3662 |
| 3663 /** |
| 3664 * The return value of the last executed listener. |
| 3665 * |
| 3666 * @memberOf Benchmark.Event |
| 3667 * @type Mixed |
| 3668 */ |
| 3669 'result': undefined, |
| 3670 |
| 3671 /** |
| 3672 * The object to which the event was originally emitted. |
| 3673 * |
| 3674 * @memberOf Benchmark.Event |
| 3675 * @type Object |
| 3676 */ |
| 3677 'target': undefined, |
| 3678 |
| 3679 /** |
| 3680 * A timestamp of when the event was created (ms). |
| 3681 * |
| 3682 * @memberOf Benchmark.Event |
| 3683 * @type Number |
| 3684 */ |
| 3685 'timeStamp': 0, |
| 3686 |
| 3687 /** |
| 3688 * The event type. |
| 3689 * |
| 3690 * @memberOf Benchmark.Event |
| 3691 * @type String |
| 3692 */ |
| 3693 'type': '' |
| 3694 }); |
| 3695 |
| 3696 /*--------------------------------------------------------------------------*/ |
| 3697 |
| 3698 /** |
| 3699 * The default options copied by suite instances. |
| 3700 * |
| 3701 * @static |
| 3702 * @memberOf Benchmark.Suite |
| 3703 * @type Object |
| 3704 */ |
| 3705 Suite.options = { |
| 3706 |
| 3707 /** |
| 3708 * The name of the suite. |
| 3709 * |
| 3710 * @memberOf Benchmark.Suite.options |
| 3711 * @type String |
| 3712 */ |
| 3713 'name': undefined |
| 3714 }; |
| 3715 |
| 3716 /*--------------------------------------------------------------------------*/ |
| 3717 |
| 3718 extend(Suite.prototype, { |
| 3719 |
| 3720 /** |
| 3721 * The number of benchmarks in the suite. |
| 3722 * |
| 3723 * @memberOf Benchmark.Suite |
| 3724 * @type Number |
| 3725 */ |
| 3726 'length': 0, |
| 3727 |
| 3728 /** |
| 3729 * A flag to indicate if the suite is aborted. |
| 3730 * |
| 3731 * @memberOf Benchmark.Suite |
| 3732 * @type Boolean |
| 3733 */ |
| 3734 'aborted': false, |
| 3735 |
| 3736 /** |
| 3737 * A flag to indicate if the suite is running. |
| 3738 * |
| 3739 * @memberOf Benchmark.Suite |
| 3740 * @type Boolean |
| 3741 */ |
| 3742 'running': false, |
| 3743 |
| 3744 /** |
| 3745 * An `Array#forEach` like method. |
| 3746 * Callbacks may terminate the loop by explicitly returning `false`. |
| 3747 * |
| 3748 * @memberOf Benchmark.Suite |
| 3749 * @param {Function} callback The function called per iteration. |
| 3750 * @returns {Object} The suite iterated over. |
| 3751 */ |
| 3752 'forEach': methodize(forEach), |
| 3753 |
| 3754 /** |
| 3755 * An `Array#indexOf` like method. |
| 3756 * |
| 3757 * @memberOf Benchmark.Suite |
| 3758 * @param {Mixed} value The value to search for. |
| 3759 * @returns {Number} The index of the matched value or `-1`. |
| 3760 */ |
| 3761 'indexOf': methodize(indexOf), |
| 3762 |
| 3763 /** |
| 3764 * Invokes a method on all benchmarks in the suite. |
| 3765 * |
| 3766 * @memberOf Benchmark.Suite |
| 3767 * @param {String|Object} name The name of the method to invoke OR options o
bject. |
| 3768 * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with. |
| 3769 * @returns {Array} A new array of values returned from each method invoked. |
| 3770 */ |
| 3771 'invoke': methodize(invoke), |
| 3772 |
| 3773 /** |
| 3774 * Converts the suite of benchmarks to a string. |
| 3775 * |
| 3776 * @memberOf Benchmark.Suite |
| 3777 * @param {String} [separator=','] A string to separate each element of the
array. |
| 3778 * @returns {String} The string. |
| 3779 */ |
| 3780 'join': [].join, |
| 3781 |
| 3782 /** |
| 3783 * An `Array#map` like method. |
| 3784 * |
| 3785 * @memberOf Benchmark.Suite |
| 3786 * @param {Function} callback The function called per iteration. |
| 3787 * @returns {Array} A new array of values returned by the callback. |
| 3788 */ |
| 3789 'map': methodize(map), |
| 3790 |
| 3791 /** |
| 3792 * Retrieves the value of a specified property from all benchmarks in the su
ite. |
| 3793 * |
| 3794 * @memberOf Benchmark.Suite |
| 3795 * @param {String} property The property to pluck. |
| 3796 * @returns {Array} A new array of property values. |
| 3797 */ |
| 3798 'pluck': methodize(pluck), |
| 3799 |
| 3800 /** |
| 3801 * Removes the last benchmark from the suite and returns it. |
| 3802 * |
| 3803 * @memberOf Benchmark.Suite |
| 3804 * @returns {Mixed} The removed benchmark. |
| 3805 */ |
| 3806 'pop': [].pop, |
| 3807 |
| 3808 /** |
| 3809 * Appends benchmarks to the suite. |
| 3810 * |
| 3811 * @memberOf Benchmark.Suite |
| 3812 * @returns {Number} The suite's new length. |
| 3813 */ |
| 3814 'push': [].push, |
| 3815 |
| 3816 /** |
| 3817 * Sorts the benchmarks of the suite. |
| 3818 * |
| 3819 * @memberOf Benchmark.Suite |
| 3820 * @param {Function} [compareFn=null] A function that defines the sort order
. |
| 3821 * @returns {Object} The sorted suite. |
| 3822 */ |
| 3823 'sort': [].sort, |
| 3824 |
| 3825 /** |
| 3826 * An `Array#reduce` like method. |
| 3827 * |
| 3828 * @memberOf Benchmark.Suite |
| 3829 * @param {Function} callback The function called per iteration. |
| 3830 * @param {Mixed} accumulator Initial value of the accumulator. |
| 3831 * @returns {Mixed} The accumulator. |
| 3832 */ |
| 3833 'reduce': methodize(reduce), |
| 3834 |
| 3835 // aborts all benchmarks in the suite |
| 3836 'abort': abortSuite, |
| 3837 |
| 3838 // adds a benchmark to the suite |
| 3839 'add': add, |
| 3840 |
| 3841 // creates a new suite with cloned benchmarks |
| 3842 'clone': cloneSuite, |
| 3843 |
| 3844 // executes listeners of a specified type |
| 3845 'emit': emit, |
| 3846 |
| 3847 // creates a new suite of filtered benchmarks |
| 3848 'filter': filterSuite, |
| 3849 |
| 3850 // get listeners |
| 3851 'listeners': listeners, |
| 3852 |
| 3853 // unregister listeners |
| 3854 'off': off, |
| 3855 |
| 3856 // register listeners |
| 3857 'on': on, |
| 3858 |
| 3859 // resets all benchmarks in the suite |
| 3860 'reset': resetSuite, |
| 3861 |
| 3862 // runs all benchmarks in the suite |
| 3863 'run': runSuite, |
| 3864 |
| 3865 // array methods |
| 3866 'concat': concat, |
| 3867 |
| 3868 'reverse': reverse, |
| 3869 |
| 3870 'shift': shift, |
| 3871 |
| 3872 'slice': slice, |
| 3873 |
| 3874 'splice': splice, |
| 3875 |
| 3876 'unshift': unshift |
| 3877 }); |
| 3878 |
| 3879 /*--------------------------------------------------------------------------*/ |
| 3880 |
| 3881 // expose Deferred, Event and Suite |
| 3882 extend(Benchmark, { |
| 3883 'Deferred': Deferred, |
| 3884 'Event': Event, |
| 3885 'Suite': Suite |
| 3886 }); |
| 3887 |
| 3888 // expose Benchmark |
| 3889 // some AMD build optimizers, like r.js, check for specific condition patterns
like the following: |
| 3890 if (typeof define == 'function' && typeof define.amd == 'object' && define.amd
) { |
| 3891 // define as an anonymous module so, through path mapping, it can be aliased |
| 3892 define(function() { |
| 3893 return Benchmark; |
| 3894 }); |
| 3895 } |
| 3896 // check for `exports` after `define` in case a build optimizer adds an `expor
ts` object |
| 3897 else if (freeExports) { |
| 3898 // in Node.js or RingoJS v0.8.0+ |
| 3899 if (typeof module == 'object' && module && module.exports == freeExports) { |
| 3900 (module.exports = Benchmark).Benchmark = Benchmark; |
| 3901 } |
| 3902 // in Narwhal or RingoJS v0.7.0- |
| 3903 else { |
| 3904 freeExports.Benchmark = Benchmark; |
| 3905 } |
| 3906 } |
| 3907 // in a browser or Rhino |
| 3908 else { |
| 3909 // use square bracket notation so Closure Compiler won't munge `Benchmark` |
| 3910 // http://code.google.com/closure/compiler/docs/api-tutorial3.html#export |
| 3911 window['Benchmark'] = Benchmark; |
| 3912 } |
| 3913 |
| 3914 // trigger clock's lazy define early to avoid a security error |
| 3915 if (support.air) { |
| 3916 clock({ '_original': { 'fn': noop, 'count': 1, 'options': {} } }); |
| 3917 } |
| 3918 }(this)); |
OLD | NEW |