| OLD | NEW |
| (Empty) |
| 1 // Copyright 2011 the V8 project authors. All rights reserved. | |
| 2 // Redistribution and use in source and binary forms, with or without | |
| 3 // modification, are permitted provided that the following conditions are | |
| 4 // met: | |
| 5 // | |
| 6 // * Redistributions of source code must retain the above copyright | |
| 7 // notice, this list of conditions and the following disclaimer. | |
| 8 // * Redistributions in binary form must reproduce the above | |
| 9 // copyright notice, this list of conditions and the following | |
| 10 // disclaimer in the documentation and/or other materials provided | |
| 11 // with the distribution. | |
| 12 // * Neither the name of Google Inc. nor the names of its | |
| 13 // contributors may be used to endorse or promote products derived | |
| 14 // from this software without specific prior written permission. | |
| 15 // | |
| 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 27 | |
| 28 | |
| 29 /** | |
| 30 * This function provides requestAnimationFrame in a cross browser way. | |
| 31 * http://paulirish.com/2011/requestanimationframe-for-smart-animating/ | |
| 32 */ | |
| 33 if ( !window.requestAnimationFrame ) { | |
| 34 window.requestAnimationFrame = ( function() { | |
| 35 return window.webkitRequestAnimationFrame || | |
| 36 window.mozRequestAnimationFrame || | |
| 37 window.oRequestAnimationFrame || | |
| 38 window.msRequestAnimationFrame || | |
| 39 function(callback, element) { | |
| 40 window.setTimeout( callback, 1000 / 60 ); | |
| 41 }; | |
| 42 } )(); | |
| 43 } | |
| 44 | |
| 45 var kNPoints = 8000; | |
| 46 var kNModifications = 20; | |
| 47 var kNVisiblePoints = 200; | |
| 48 var kDecaySpeed = 20; | |
| 49 | |
| 50 var kPointRadius = 4; | |
| 51 var kInitialLifeForce = 100; | |
| 52 | |
| 53 var livePoints = void 0; | |
| 54 var dyingPoints = void 0; | |
| 55 var scene = void 0; | |
| 56 var renderingStartTime = void 0; | |
| 57 var scene = void 0; | |
| 58 var pausePlot = void 0; | |
| 59 var splayTree = void 0; | |
| 60 var numberOfFrames = 0; | |
| 61 var sumOfSquaredPauses = 0; | |
| 62 var benchmarkStartTime = void 0; | |
| 63 var benchmarkTimeLimit = void 0; | |
| 64 var autoScale = void 0; | |
| 65 var pauseDistribution = []; | |
| 66 | |
| 67 | |
| 68 function Point(x, y, z, payload) { | |
| 69 this.x = x; | |
| 70 this.y = y; | |
| 71 this.z = z; | |
| 72 | |
| 73 this.next = null; | |
| 74 this.prev = null; | |
| 75 this.payload = payload; | |
| 76 this.lifeForce = kInitialLifeForce; | |
| 77 } | |
| 78 | |
| 79 | |
| 80 Point.prototype.color = function () { | |
| 81 return "rgba(0, 0, 0, " + (this.lifeForce / kInitialLifeForce) + ")"; | |
| 82 }; | |
| 83 | |
| 84 | |
| 85 Point.prototype.decay = function () { | |
| 86 this.lifeForce -= kDecaySpeed; | |
| 87 return this.lifeForce <= 0; | |
| 88 }; | |
| 89 | |
| 90 | |
| 91 function PointsList() { | |
| 92 this.head = null; | |
| 93 this.count = 0; | |
| 94 } | |
| 95 | |
| 96 | |
| 97 PointsList.prototype.add = function (point) { | |
| 98 if (this.head !== null) this.head.prev = point; | |
| 99 point.next = this.head; | |
| 100 this.head = point; | |
| 101 this.count++; | |
| 102 } | |
| 103 | |
| 104 | |
| 105 PointsList.prototype.remove = function (point) { | |
| 106 if (point.next !== null) { | |
| 107 point.next.prev = point.prev; | |
| 108 } | |
| 109 if (point.prev !== null) { | |
| 110 point.prev.next = point.next; | |
| 111 } else { | |
| 112 this.head = point.next; | |
| 113 } | |
| 114 this.count--; | |
| 115 } | |
| 116 | |
| 117 | |
| 118 function GeneratePayloadTree(depth, tag) { | |
| 119 if (depth == 0) { | |
| 120 return { | |
| 121 array : [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], | |
| 122 string : 'String for key ' + tag + ' in leaf node' | |
| 123 }; | |
| 124 } else { | |
| 125 return { | |
| 126 left: GeneratePayloadTree(depth - 1, tag), | |
| 127 right: GeneratePayloadTree(depth - 1, tag) | |
| 128 }; | |
| 129 } | |
| 130 } | |
| 131 | |
| 132 | |
| 133 // To make the benchmark results predictable, we replace Math.random | |
| 134 // with a 100% deterministic alternative. | |
| 135 Math.random = (function() { | |
| 136 var seed = 49734321; | |
| 137 return function() { | |
| 138 // Robert Jenkins' 32 bit integer hash function. | |
| 139 seed = ((seed + 0x7ed55d16) + (seed << 12)) & 0xffffffff; | |
| 140 seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff; | |
| 141 seed = ((seed + 0x165667b1) + (seed << 5)) & 0xffffffff; | |
| 142 seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff; | |
| 143 seed = ((seed + 0xfd7046c5) + (seed << 3)) & 0xffffffff; | |
| 144 seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff; | |
| 145 return (seed & 0xfffffff) / 0x10000000; | |
| 146 }; | |
| 147 })(); | |
| 148 | |
| 149 | |
| 150 function GenerateKey() { | |
| 151 // The benchmark framework guarantees that Math.random is | |
| 152 // deterministic; see base.js. | |
| 153 return Math.random(); | |
| 154 } | |
| 155 | |
| 156 function CreateNewPoint() { | |
| 157 // Insert new node with a unique key. | |
| 158 var key; | |
| 159 do { key = GenerateKey(); } while (splayTree.find(key) != null); | |
| 160 | |
| 161 var point = new Point(Math.random() * 40 - 20, | |
| 162 Math.random() * 40 - 20, | |
| 163 Math.random() * 40 - 20, | |
| 164 GeneratePayloadTree(5, "" + key)); | |
| 165 | |
| 166 livePoints.add(point); | |
| 167 | |
| 168 splayTree.insert(key, point); | |
| 169 return key; | |
| 170 } | |
| 171 | |
| 172 function ModifyPointsSet() { | |
| 173 if (livePoints.count < kNPoints) { | |
| 174 for (var i = 0; i < kNModifications; i++) { | |
| 175 CreateNewPoint(); | |
| 176 } | |
| 177 } else if (kNModifications === 20) { | |
| 178 kNModifications = 80; | |
| 179 kDecay = 30; | |
| 180 } | |
| 181 | |
| 182 for (var i = 0; i < kNModifications; i++) { | |
| 183 var key = CreateNewPoint(); | |
| 184 var greatest = splayTree.findGreatestLessThan(key); | |
| 185 if (greatest == null) { | |
| 186 var point = splayTree.remove(key).value; | |
| 187 } else { | |
| 188 var point = splayTree.remove(greatest.key).value; | |
| 189 } | |
| 190 livePoints.remove(point); | |
| 191 point.payload = null; | |
| 192 dyingPoints.add(point); | |
| 193 } | |
| 194 } | |
| 195 | |
| 196 | |
| 197 function PausePlot(width, height, size, scale) { | |
| 198 var canvas = document.createElement("canvas"); | |
| 199 canvas.width = this.width = width; | |
| 200 canvas.height = this.height = height; | |
| 201 document.body.appendChild(canvas); | |
| 202 | |
| 203 this.ctx = canvas.getContext('2d'); | |
| 204 | |
| 205 if (typeof scale !== "number") { | |
| 206 this.autoScale = true; | |
| 207 this.maxPause = 0; | |
| 208 } else { | |
| 209 this.autoScale = false; | |
| 210 this.maxPause = scale; | |
| 211 } | |
| 212 | |
| 213 this.size = size; | |
| 214 | |
| 215 // Initialize cyclic buffer for pauses. | |
| 216 this.pauses = new Array(this.size); | |
| 217 this.start = this.size; | |
| 218 this.idx = 0; | |
| 219 } | |
| 220 | |
| 221 | |
| 222 PausePlot.prototype.addPause = function (p) { | |
| 223 if (this.idx === this.size) { | |
| 224 this.idx = 0; | |
| 225 } | |
| 226 | |
| 227 if (this.idx === this.start) { | |
| 228 this.start++; | |
| 229 } | |
| 230 | |
| 231 if (this.start === this.size) { | |
| 232 this.start = 0; | |
| 233 } | |
| 234 | |
| 235 this.pauses[this.idx++] = p; | |
| 236 }; | |
| 237 | |
| 238 | |
| 239 PausePlot.prototype.iteratePauses = function (f) { | |
| 240 if (this.start < this.idx) { | |
| 241 for (var i = this.start; i < this.idx; i++) { | |
| 242 f.call(this, i - this.start, this.pauses[i]); | |
| 243 } | |
| 244 } else { | |
| 245 for (var i = this.start; i < this.size; i++) { | |
| 246 f.call(this, i - this.start, this.pauses[i]); | |
| 247 } | |
| 248 | |
| 249 var offs = this.size - this.start; | |
| 250 for (var i = 0; i < this.idx; i++) { | |
| 251 f.call(this, i + offs, this.pauses[i]); | |
| 252 } | |
| 253 } | |
| 254 }; | |
| 255 | |
| 256 | |
| 257 PausePlot.prototype.draw = function () { | |
| 258 var first = null; | |
| 259 | |
| 260 if (this.autoScale) { | |
| 261 this.iteratePauses(function (i, v) { | |
| 262 if (first === null) { | |
| 263 first = v; | |
| 264 } | |
| 265 this.maxPause = Math.max(v, this.maxPause); | |
| 266 }); | |
| 267 } | |
| 268 | |
| 269 var dx = this.width / this.size; | |
| 270 var dy = this.height / this.maxPause; | |
| 271 | |
| 272 this.ctx.save(); | |
| 273 this.ctx.clearRect(0, 0, this.width, this.height); | |
| 274 this.ctx.beginPath(); | |
| 275 this.ctx.moveTo(1, dy * this.pauses[this.start]); | |
| 276 var p = first; | |
| 277 this.iteratePauses(function (i, v) { | |
| 278 var delta = v - p; | |
| 279 var x = 1 + dx * i; | |
| 280 var y = dy * v; | |
| 281 this.ctx.lineTo(x, y); | |
| 282 if (delta > 2 * (p / 3)) { | |
| 283 this.ctx.font = "bold 12px sans-serif"; | |
| 284 this.ctx.textBaseline = "bottom"; | |
| 285 this.ctx.fillText(v + "ms", x + 2, y); | |
| 286 } | |
| 287 p = v; | |
| 288 }); | |
| 289 this.ctx.strokeStyle = "black"; | |
| 290 this.ctx.stroke(); | |
| 291 this.ctx.restore(); | |
| 292 } | |
| 293 | |
| 294 | |
| 295 function Scene(width, height) { | |
| 296 var canvas = document.createElement("canvas"); | |
| 297 canvas.width = width; | |
| 298 canvas.height = height; | |
| 299 document.body.appendChild(canvas); | |
| 300 | |
| 301 this.ctx = canvas.getContext('2d'); | |
| 302 this.width = canvas.width; | |
| 303 this.height = canvas.height; | |
| 304 | |
| 305 // Projection configuration. | |
| 306 this.x0 = canvas.width / 2; | |
| 307 this.y0 = canvas.height / 2; | |
| 308 this.z0 = 100; | |
| 309 this.f = 1000; // Focal length. | |
| 310 | |
| 311 // Camera is rotating around y-axis. | |
| 312 this.angle = 0; | |
| 313 } | |
| 314 | |
| 315 | |
| 316 Scene.prototype.drawPoint = function (x, y, z, color) { | |
| 317 // Rotate the camera around y-axis. | |
| 318 var rx = x * Math.cos(this.angle) - z * Math.sin(this.angle); | |
| 319 var ry = y; | |
| 320 var rz = x * Math.sin(this.angle) + z * Math.cos(this.angle); | |
| 321 | |
| 322 // Perform perspective projection. | |
| 323 var px = (this.f * rx) / (rz - this.z0) + this.x0; | |
| 324 var py = (this.f * ry) / (rz - this.z0) + this.y0; | |
| 325 | |
| 326 this.ctx.save(); | |
| 327 this.ctx.fillStyle = color | |
| 328 this.ctx.beginPath(); | |
| 329 this.ctx.arc(px, py, kPointRadius, 0, 2 * Math.PI, true); | |
| 330 this.ctx.fill(); | |
| 331 this.ctx.restore(); | |
| 332 }; | |
| 333 | |
| 334 | |
| 335 Scene.prototype.drawDyingPoints = function () { | |
| 336 var point_next = null; | |
| 337 for (var point = dyingPoints.head; point !== null; point = point_next) { | |
| 338 // Rotate the scene around y-axis. | |
| 339 scene.drawPoint(point.x, point.y, point.z, point.color()); | |
| 340 | |
| 341 point_next = point.next; | |
| 342 | |
| 343 // Decay the current point and remove it from the list | |
| 344 // if it's life-force ran out. | |
| 345 if (point.decay()) { | |
| 346 dyingPoints.remove(point); | |
| 347 } | |
| 348 } | |
| 349 }; | |
| 350 | |
| 351 | |
| 352 Scene.prototype.draw = function () { | |
| 353 this.ctx.save(); | |
| 354 this.ctx.clearRect(0, 0, this.width, this.height); | |
| 355 this.drawDyingPoints(); | |
| 356 this.ctx.restore(); | |
| 357 | |
| 358 this.angle += Math.PI / 90.0; | |
| 359 }; | |
| 360 | |
| 361 | |
| 362 function updateStats(pause) { | |
| 363 numberOfFrames++; | |
| 364 if (pause > 20) { | |
| 365 sumOfSquaredPauses += (pause - 20) * (pause - 20); | |
| 366 } | |
| 367 pauseDistribution[Math.floor(pause / 10)] |= 0; | |
| 368 pauseDistribution[Math.floor(pause / 10)]++; | |
| 369 } | |
| 370 | |
| 371 | |
| 372 function renderStats() { | |
| 373 var msg = document.createElement("p"); | |
| 374 msg.innerHTML = "Score " + | |
| 375 Math.round(numberOfFrames * 1000 / sumOfSquaredPauses); | |
| 376 var table = document.createElement("table"); | |
| 377 table.align = "center"; | |
| 378 for (var i = 0; i < pauseDistribution.length; i++) { | |
| 379 if (pauseDistribution[i] > 0) { | |
| 380 var row = document.createElement("tr"); | |
| 381 var time = document.createElement("td"); | |
| 382 var count = document.createElement("td"); | |
| 383 time.innerHTML = i*10 + "-" + (i+1)*10 + "ms"; | |
| 384 count.innerHTML = " => " + pauseDistribution[i]; | |
| 385 row.appendChild(time); | |
| 386 row.appendChild(count); | |
| 387 table.appendChild(row); | |
| 388 } | |
| 389 } | |
| 390 div.appendChild(msg); | |
| 391 div.appendChild(table); | |
| 392 } | |
| 393 | |
| 394 | |
| 395 function render() { | |
| 396 if (typeof renderingStartTime === 'undefined') { | |
| 397 renderingStartTime = Date.now(); | |
| 398 benchmarkStartTime = renderingStartTime; | |
| 399 } | |
| 400 | |
| 401 ModifyPointsSet(); | |
| 402 | |
| 403 scene.draw(); | |
| 404 | |
| 405 var renderingEndTime = Date.now(); | |
| 406 var pause = renderingEndTime - renderingStartTime; | |
| 407 pausePlot.addPause(pause); | |
| 408 renderingStartTime = renderingEndTime; | |
| 409 | |
| 410 pausePlot.draw(); | |
| 411 | |
| 412 updateStats(pause); | |
| 413 | |
| 414 div.innerHTML = | |
| 415 livePoints.count + "/" + dyingPoints.count + " " + | |
| 416 pause + "(max = " + pausePlot.maxPause + ") ms " + | |
| 417 numberOfFrames + " frames"; | |
| 418 | |
| 419 if (renderingEndTime < benchmarkStartTime + benchmarkTimeLimit) { | |
| 420 // Schedule next frame. | |
| 421 requestAnimationFrame(render); | |
| 422 } else { | |
| 423 renderStats(); | |
| 424 } | |
| 425 } | |
| 426 | |
| 427 | |
| 428 function Form() { | |
| 429 function create(tag) { return document.createElement(tag); } | |
| 430 function text(value) { return document.createTextNode(value); } | |
| 431 | |
| 432 this.form = create("form"); | |
| 433 this.form.setAttribute("action", "javascript:start()"); | |
| 434 | |
| 435 var table = create("table"); | |
| 436 table.setAttribute("style", "margin-left: auto; margin-right: auto;"); | |
| 437 | |
| 438 function col(a) { | |
| 439 var td = create("td"); | |
| 440 td.appendChild(a); | |
| 441 return td; | |
| 442 } | |
| 443 | |
| 444 function row(a, b) { | |
| 445 var tr = create("tr"); | |
| 446 tr.appendChild(col(a)); | |
| 447 tr.appendChild(col(b)); | |
| 448 return tr; | |
| 449 } | |
| 450 | |
| 451 this.timelimit = create("input"); | |
| 452 this.timelimit.setAttribute("value", "60"); | |
| 453 | |
| 454 table.appendChild(row(text("Time limit in seconds"), this.timelimit)); | |
| 455 | |
| 456 this.autoscale = create("input"); | |
| 457 this.autoscale.setAttribute("type", "checkbox"); | |
| 458 this.autoscale.setAttribute("checked", "true"); | |
| 459 table.appendChild(row(text("Autoscale pauses plot"), this.autoscale)); | |
| 460 | |
| 461 var button = create("input"); | |
| 462 button.setAttribute("type", "submit"); | |
| 463 button.setAttribute("value", "Start"); | |
| 464 this.form.appendChild(table); | |
| 465 this.form.appendChild(button); | |
| 466 | |
| 467 document.body.appendChild(this.form); | |
| 468 } | |
| 469 | |
| 470 | |
| 471 Form.prototype.remove = function () { | |
| 472 document.body.removeChild(this.form); | |
| 473 }; | |
| 474 | |
| 475 | |
| 476 function init() { | |
| 477 livePoints = new PointsList; | |
| 478 dyingPoints = new PointsList; | |
| 479 | |
| 480 splayTree = new SplayTree(); | |
| 481 | |
| 482 scene = new Scene(640, 480); | |
| 483 | |
| 484 div = document.createElement("div"); | |
| 485 document.body.appendChild(div); | |
| 486 | |
| 487 pausePlot = new PausePlot(480, autoScale ? 240 : 500, 160, autoScale ? void 0
: 500); | |
| 488 } | |
| 489 | |
| 490 function start() { | |
| 491 benchmarkTimeLimit = form.timelimit.value * 1000; | |
| 492 autoScale = form.autoscale.checked; | |
| 493 form.remove(); | |
| 494 init(); | |
| 495 render(); | |
| 496 } | |
| 497 | |
| 498 var form = new Form(); | |
| OLD | NEW |