UNPKG

dojox

Version:

Dojo eXtensions, a rollup of many useful sub-projects and varying states of maturity – from very stable and robust, to alpha and experimental. See individual projects contain README files for details.

481 lines (434 loc) 22.2 kB
<html> <head> <title>OO/decl benchmark</title> <style type="text/css"> @import "../../../dojo/resources/dojo.css"; .stats th, .stats td { padding: 3pt; } .stats th, .name { font-weight: bold; } table.stats, .stats th, .stats td { border: 1px solid lightgray; } .median { color: navy; font-weight: bold; } .fastest { background-color: #ccf; } .stablest { background-color: #ffc; } .fastest.stablest { background-color: #cfc; } </style> <script type="text/javascript" src="../../../dojo/dojo.js" data-dojo-config="isDebug:true"></script> <script type="text/javascript"> dojo.require("dojox.lang.tests.declare-old"); var d = dojo, oo = dojox.lang.oo, nothing = function(){}; // test harness var DELAY = 20, // pause in ms between tests LIMIT = 50, // the lower limit of a test COUNT = 50; // how many times to repeat the test // the basic unit to run a test with timing var runTest = function(f, n){ var start = new Date(); for(var i = 0; i < n; ++i){ f(); } var end = new Date(); return end.getTime() - start.getTime(); }; // find the threshold number of tests just exceeding the limit var findThreshold = function(f, limit){ // very simplistic search probing only powers of two var n = 1; while(runTest(f, n) + runTest(nothing, n) < limit) n <<= 1; return n; }; var _runUnitTest = function(a, f, n, k, m, next){ a[k++] = runTest(f, n) - runTest(nothing, n); if(k < m){ setTimeout(d.hitch(null, _runUnitTest, a, f, n, k, m, next), DELAY); }else{ next(a); } }; var runTests = function(f, n, m, next){ var a = new Array(m); _runUnitTest(a, f, n, 0, m, next); }; // statistics var statNames = ["minimum", "firstDecile", "lowerQuartile", "median", "upperQuartile", "lastDecile", "maximum", "average"]; var statAbbr = { minimum: "min", maximum: "max", median: "med", lowerQuartile: "25%", upperQuartile: "75%", firstDecile: "10%", lastDecile: "90%", average: "avg" }; var getWeightedValue = function(a, pos){ var p = pos * (a.length - 1), t = Math.ceil(p), f = t - 1; if(f <= 0){ return a[0]; } if(t >= a.length){ return a[a.length - 1]; } return a[f] * (t - p) + a[t] * (p - f); }; var getStats = function(a, n){ var t = a.slice(0); t.sort(function(a, b){ return a - b; }); var result = { // the five-number summary minimum: t[0], maximum: t[t.length - 1], median: getWeightedValue(t, 0.5), lowerQuartile: getWeightedValue(t, 0.25), upperQuartile: getWeightedValue(t, 0.75), // extended to the Bowley's seven-figure summary firstDecile: getWeightedValue(t, 0.1), lastDecile: getWeightedValue(t, 0.9) }; // add the average for(var i = 0, sum = 0; i < t.length; sum += t[i++]); result.average = sum / t.length; d.forEach(statNames, function(name){ if(result.hasOwnProperty(name) && typeof result[name] == "number"){ result[name] /= n; } }); return result; }; var testGroups = []; // run a group of tests, prepare statistics and show results var registerGroup = function(fs, bi, m, node, title){ var n = findThreshold(fs[bi].fun, LIMIT), x = { functions: fs, stats: [], process: function(a){ if(a){ this.stats.push(getStats(a, n)); console.log("test #" + this.stats.length + " is completed: " + this.functions[this.stats.length - 1].name); } if(this.stats.length < this.functions.length){ //setTimeout(d.hitch(null, runTests, this.functions[this.stats.length].fun, n, m, d.hitch(this, "process")), DELAY); var f = d.hitch(null, runTests, this.functions[this.stats.length].fun, n, m, d.hitch(this, "process")); f(); return; } var diff = Math.max.apply(Math, d.map(this.stats, function(s){ return s.upperQuartile - s.lowerQuartile; })), prec = 1 - Math.floor(Math.log(diff) / Math.LN10), fastest = 0, stablest = 0; d.forEach(this.stats, function(s, i){ if(i){ if(s.median < this.stats[fastest].median){ fastest = i; } if(s.upperQuartile - s.lowerQuartile < this.stats[i].upperQuartile - this.stats[i].lowerQuartile){ stablest = i; } } }, this); // add the table var tab = ["<table class='stats'><thead><tr><th>Test</th>"]; tab.push(d.map(this.functions, function(f, i){ return "<th class='" + (i == fastest ? "fastest" : "") + " " + (i == stablest ? "stablest" : "") + "'>" + f.name + "</th>"; }).join("")); tab.push("</tr></thead><tbody>"); d.forEach(statNames, function(n){ tab.push("<tr class='name " + n + "'><td>" + n + "</td>"); d.forEach(this.stats, function(s, i){ tab.push("<td class='" + (i == fastest ? "fastest" : "") + " " + (i == stablest ? "stablest" : "") + "'>" + s[n].toFixed(prec) + "</td>"); }, this); tab.push("</tr>"); }, this); tab.push("</tbody></table>"); d.place(tab.join(""), node); // next run(); } }; testGroups.push(function(){ console.log("all tests will be repeated " + n + " times in " + m + " series"); d.place("<h1>" + title + "</h1>", node); x.process(); }); }; function run(){ if(testGroups.length){ testGroups.shift()(); }else{ setTimeout(function(){ console.log("Done!"); alert("Done!"); }, DELAY); } } // actual benchmarks var decl0 = dojox.lang.tests.declareOld, declx = d.declare; var benchmarkClassCreation = function(){ var a0 = decl0("temp.A0", null, {m1: function(){}, m2: function(){}, m3: function(){}}), b0 = decl0("temp.B0", a0, {m1: function(){}, m2: function(){}, m3: function(){}}), c0 = decl0("temp.C0", b0, {m1: function(){}, m2: function(){}, m3: function(){}}), ax = declx("temp.Ax", null, {m1: function(){}, m2: function(){}, m3: function(){}}), bx = declx("temp.Bx", ax, {m1: function(){}, m2: function(){}, m3: function(){}}), cx = declx("temp.Cx", bx, {m1: function(){}, m2: function(){}, m3: function(){}}), group = [ { name: "old/A", fun: function(){ decl0("temp.test.A", null, {m1: function(){}, m2: function(){}, m3: function(){}}); } }, { name: "new/A", fun: function(){ declx("temp.test.A", null, {m1: function(){}, m2: function(){}, m3: function(){}}); } }, { name: "old/B", fun: function(){ decl0("temp.test.B", temp.A0, {m1: function(){}, m2: function(){}, m3: function(){}}); } }, { name: "new/B", fun: function(){ declx("temp.test.B", temp.Ax, {m1: function(){}, m2: function(){}, m3: function(){}}); } }, { name: "old/C", fun: function(){ decl0("temp.test.C", temp.B0, {m1: function(){}, m2: function(){}, m3: function(){}}); } }, { name: "new/C", fun: function(){ declx("temp.test.C", temp.Bx, {m1: function(){}, m2: function(){}, m3: function(){}}); } }, { name: "old/D", fun: function(){ decl0("temp.test.D", temp.C0, {m1: function(){}, m2: function(){}, m3: function(){}}); } }, { name: "new/D", fun: function(){ declx("temp.test.D", temp.Cx, {m1: function(){}, m2: function(){}, m3: function(){}}); } } ]; registerGroup(group, 0, COUNT, "result", "Create a class (single inheritance)"); }; var benchmarkClassMixing = function(){ var a0 = decl0("temp.A0", null, {m1: function(){}, m2: function(){}, m3: function(){}}), b0 = decl0("temp.B0", null, {m1: function(){}, m2: function(){}, m3: function(){}}), c0 = decl0("temp.C0", null, {m1: function(){}, m2: function(){}, m3: function(){}}), ax = declx("temp.Ax", null, {m1: function(){}, m2: function(){}, m3: function(){}}), bx = declx("temp.Bx", null, {m1: function(){}, m2: function(){}, m3: function(){}}), cx = declx("temp.Cx", null, {m1: function(){}, m2: function(){}, m3: function(){}}), group = [ { name: "old/A", fun: function(){ decl0("temp.test.A", temp.A0, {m1: function(){}, m2: function(){}, m3: function(){}}); } }, { name: "new/A", fun: function(){ declx("temp.test.A", temp.Ax, {m1: function(){}, m2: function(){}, m3: function(){}}); } }, { name: "old/A,B", fun: function(){ decl0("temp.test.B", [temp.A0, temp.B0], {m1: function(){}, m2: function(){}, m3: function(){}}); } }, { name: "new/A,B", fun: function(){ declx("temp.test.B", [temp.Ax, temp.Bx], {m1: function(){}, m2: function(){}, m3: function(){}}); } }, { name: "old/A,B,C", fun: function(){ decl0("temp.test.C", [temp.A0, temp.B0, temp.C0], {m1: function(){}, m2: function(){}, m3: function(){}}); } }, { name: "new/A,B,C", fun: function(){ declx("temp.test.C", [temp.Ax, temp.Bx, temp.Cx], {m1: function(){}, m2: function(){}, m3: function(){}}); } } ]; registerGroup(group, 0, COUNT, "result", "Create a class with mixins"); }; var benchmarkConstructor = function(){ var A0 = decl0("temp.A0", null, {constructor: function(a){ this.a = a; }}), B0 = decl0("temp.B0", A0, {constructor: function(a, b){ this.b = b; }}), C0 = decl0("temp.C0", B0, {constructor: function(a, b, c){ this.c = c; }}), D0 = decl0("temp.D0", C0, {constructor: function(a, b, c, d){ this.d = d; }}), Ax = declx("temp.Ax", null, {constructor: function(a){ this.a = a; }}), Bx = declx("temp.Bx", Ax, {constructor: function(a, b){ this.b = b; }}), Cx = declx("temp.Cx", Bx, {constructor: function(a, b, c){ this.c = c; }}), Dx = declx("temp.Dx", Cx, {constructor: function(a, b, c, d){ this.d = d; }}), group = [ { name: "old/A", fun: function(){ var t = new A0("a"); } }, { name: "new/A", fun: function(){ var t = new Ax("a"); } }, { name: "old/B", fun: function(){ var t = new B0("a", "b"); } }, { name: "new/B", fun: function(){ var t = new Bx("a", "b"); } }, { name: "old/C", fun: function(){ var t = new C0("a", "b", "c"); } }, { name: "new/C", fun: function(){ var t = new Cx("a", "b", "c"); } }, { name: "old/D", fun: function(){ var t = new D0("a", "b", "c", "d"); } }, { name: "new/D", fun: function(){ var t = new Dx("a", "b", "c", "d"); } } ]; registerGroup(group, 0, COUNT, "result", "Create an instance"); }; var benchmarkRegularCalls = function(){ var A0 = decl0("temp.A0", null, {ma: function(a){ return this.a = a; }}), B0 = decl0("temp.B0", A0, {mb: function(b){ return this.b = b; }}), C0 = decl0("temp.C0", B0, {mc: function(c){ return this.c = c; }}), D0 = decl0("temp.D0", C0, {md: function(d){ return this.d = d; }}), Ax = declx("temp.Ax", null, {ma: function(a){ return this.a = a; }}), Bx = declx("temp.Bx", Ax, {mb: function(b){ return this.b = b; }}), Cx = declx("temp.Cx", Bx, {mc: function(c){ return this.c = c; }}), Dx = declx("temp.Dx", Cx, {md: function(d){ return this.d = d; }}), d0 = new D0, dx = new Dx, group = [ { name: "old/A", fun: function(){ d0.ma("x"); } }, { name: "new/A", fun: function(){ dx.ma("x"); } }, { name: "old/B", fun: function(){ d0.mb("x"); } }, { name: "new/B", fun: function(){ dx.mb("x"); } }, { name: "old/C", fun: function(){ d0.mc("x"); } }, { name: "new/C", fun: function(){ dx.mc("x"); } }, { name: "old/D", fun: function(){ d0.md("x"); } }, { name: "new/D", fun: function(){ dx.md("x"); } } ]; registerGroup(group, 0, COUNT, "result", "Call a method"); }; var benchmarkInheritedCalls = function(){ var A0 = decl0("temp.A0", null, {m: function(a){ return this.a = a; }}), B0 = decl0("temp.B0", A0, {m: function(b){ return this.b = this.inherited(arguments); }}), C0 = decl0("temp.C0", B0, {m: function(c){ return this.c = this.inherited(arguments); }}), D0 = decl0("temp.D0", C0, {m: function(d){ return this.d = this.inherited(arguments); }}), Ax = declx("temp.Ax", null, {m: function(a){ return this.a = a; }}), Bx = declx("temp.Bx", Ax, {m: function(b){ return this.b = this.inherited(arguments); }}), Cx = declx("temp.Cx", Bx, {m: function(c){ return this.c = this.inherited(arguments); }}), Dx = declx("temp.Dx", Cx, {m: function(d){ return this.d = this.inherited(arguments); }}), b0 = new B0, bx = new Bx, c0 = new C0, cx = new Cx, d0 = new D0, dx = new Dx, group = [ { name: "old/B", fun: function(){ b0.m("x"); } }, { name: "new/B", fun: function(){ bx.m("x"); } }, { name: "old/C", fun: function(){ c0.m("x"); } }, { name: "new/C", fun: function(){ cx.m("x"); } }, { name: "old/D", fun: function(){ d0.m("x"); } }, { name: "new/D", fun: function(){ dx.m("x"); } } ]; registerGroup(group, 0, COUNT, "result", "Call an inherited method"); }; var benchmarkChains = function(){ var A0 = decl0("temp.A0", null, {m: function(a){ this.a = a; }}), B0 = decl0("temp.B0", A0, {m: function(b){ this.inherited(arguments); this.b = b; }}), C0 = decl0("temp.C0", B0, {m: function(c){ this.inherited(arguments); this.c = c; }}), D0 = decl0("temp.D0", C0, {m: function(d){ this.inherited(arguments); this.d = d; }}), Ax = declx("temp.Ax", null, {m: function(a){ this.a = a; }, "-chains-": {m: "after"}}), Bx = declx("temp.Bx", Ax, {m: function(b){ this.b = b; }}), Cx = declx("temp.Cx", Bx, {m: function(c){ this.c = c; }}), Dx = declx("temp.Dx", Cx, {m: function(d){ this.d = d; }}), b0 = new B0, bx = new Bx, c0 = new C0, cx = new Cx, d0 = new D0, dx = new Dx, group = [ { name: "old/B", fun: function(){ b0.m("x"); } }, { name: "new/B", fun: function(){ bx.m("x"); } }, { name: "old/C", fun: function(){ c0.m("x"); } }, { name: "new/C", fun: function(){ cx.m("x"); } }, { name: "old/D", fun: function(){ d0.m("x"); } }, { name: "new/D", fun: function(){ dx.m("x"); } } ]; registerGroup(group, 0, COUNT, "result", "Call a chain"); }; var startBenchmarks = function(){ console.log("Used parameters: count=" + COUNT + " limit=" + LIMIT + "ms delay=" + DELAY + "ms"); benchmarkClassCreation(); benchmarkClassMixing(); benchmarkConstructor(); //benchmarkRegularCalls(); benchmarkInheritedCalls(); benchmarkChains(); run(); }; //dojo.addOnLoad(startBenchmarks); </script> </head> <body> <p>Warning: the benchmark takes several minutes, wait for a dialog box.</p> <p>Color legend: <span class="fastest">the fastest</span>, <span class="stablest">the most stable</span>, <span class="fastest stablest">the fastest and the most stable</span></p> <p><button onclick="startBenchmarks()">Start</button></p> <div id="result"></div> </body> </html>