summaryrefslogtreecommitdiffstats
path: root/js/src/devtools/gc-ubench
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /js/src/devtools/gc-ubench
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/devtools/gc-ubench')
-rw-r--r--js/src/devtools/gc-ubench/benchmarks/bigTextNodes.js25
-rw-r--r--js/src/devtools/gc-ubench/benchmarks/events.js25
-rw-r--r--js/src/devtools/gc-ubench/benchmarks/expandoEvents.js26
-rw-r--r--js/src/devtools/gc-ubench/benchmarks/globalArrayArrayLiteral.js16
-rw-r--r--js/src/devtools/gc-ubench/benchmarks/globalArrayBuffer.js23
-rw-r--r--js/src/devtools/gc-ubench/benchmarks/globalArrayFgFinalized.js23
-rw-r--r--js/src/devtools/gc-ubench/benchmarks/globalArrayLargeArray.js18
-rw-r--r--js/src/devtools/gc-ubench/benchmarks/globalArrayLargeObject.js23
-rw-r--r--js/src/devtools/gc-ubench/benchmarks/globalArrayNewObject.js16
-rw-r--r--js/src/devtools/gc-ubench/benchmarks/globalArrayObjectLiteral.js16
-rw-r--r--js/src/devtools/gc-ubench/benchmarks/globalArrayReallocArray.js18
-rw-r--r--js/src/devtools/gc-ubench/benchmarks/largeArrayPropertyAndElements.js34
-rw-r--r--js/src/devtools/gc-ubench/benchmarks/noAllocation.js6
-rw-r--r--js/src/devtools/gc-ubench/benchmarks/pairCyclicWeakMap.js33
-rw-r--r--js/src/devtools/gc-ubench/benchmarks/propertyTreeSplitting.js25
-rw-r--r--js/src/devtools/gc-ubench/benchmarks/selfCyclicWeakMap.js29
-rw-r--r--js/src/devtools/gc-ubench/benchmarks/textNodes.js23
-rw-r--r--js/src/devtools/gc-ubench/harness.js661
-rw-r--r--js/src/devtools/gc-ubench/index.html84
19 files changed, 1124 insertions, 0 deletions
diff --git a/js/src/devtools/gc-ubench/benchmarks/bigTextNodes.js b/js/src/devtools/gc-ubench/benchmarks/bigTextNodes.js
new file mode 100644
index 000000000..1a6119f56
--- /dev/null
+++ b/js/src/devtools/gc-ubench/benchmarks/bigTextNodes.js
@@ -0,0 +1,25 @@
+window.tests.set('bigTextNodes', (function() {
+var garbage = [];
+var garbageIndex = 0;
+return {
+ description: "var foo = [ textNode, textNode, ... ]",
+
+ load: (N) => { garbage = new Array(N); },
+ unload: () => { garbage = []; garbageIndex = 0; },
+
+ defaultGarbagePerFrame: "8",
+ defaultGarbageTotal: "8",
+
+ makeGarbage: (N) => {
+ var a = [];
+ var s = "x";
+ for (var i = 0; i < 16; i++)
+ s = s + s;
+ for (var i = 0; i < N; i++)
+ a.push(document.createTextNode(s));
+ garbage[garbageIndex++] = a;
+ if (garbageIndex == garbage.length)
+ garbageIndex = 0;
+ }
+};
+})());
diff --git a/js/src/devtools/gc-ubench/benchmarks/events.js b/js/src/devtools/gc-ubench/benchmarks/events.js
new file mode 100644
index 000000000..37f91f754
--- /dev/null
+++ b/js/src/devtools/gc-ubench/benchmarks/events.js
@@ -0,0 +1,25 @@
+window.tests.set('events', (function() {
+var garbage = [];
+var garbageIndex = 0;
+return {
+ description: "var foo = [ textNode, textNode, ... ]",
+
+ load: (N) => { garbage = new Array(N); },
+ unload: () => { garbage = []; garbageIndex = 0; },
+
+ defaultGarbagePerFrame: "100K",
+ defaultGarbageTotal: "8",
+
+ makeGarbage: (N) => {
+ var a = [];
+ for (var i = 0; i < N; i++) {
+ var e = document.createEvent("Events");
+ e.initEvent("TestEvent", true, true);
+ a.push(e);
+ }
+ garbage[garbageIndex++] = a;
+ if (garbageIndex == garbage.length)
+ garbageIndex = 0;
+ }
+};
+})());
diff --git a/js/src/devtools/gc-ubench/benchmarks/expandoEvents.js b/js/src/devtools/gc-ubench/benchmarks/expandoEvents.js
new file mode 100644
index 000000000..70ff780c4
--- /dev/null
+++ b/js/src/devtools/gc-ubench/benchmarks/expandoEvents.js
@@ -0,0 +1,26 @@
+window.tests.set('expandoEvents', (function() {
+var garbage = [];
+var garbageIndex = 0;
+return {
+ description: "var foo = [ textNode, textNode, ... ]",
+
+ load: (N) => { garbage = new Array(N); },
+ unload: () => { garbage = []; garbageIndex = 0; },
+
+ defaultGarbagePerFrame: "100K",
+ defaultGarbageTotal: "8",
+
+ makeGarbage: (N) => {
+ var a = [];
+ for (var i = 0; i < N; i++) {
+ var e = document.createEvent("Events");
+ e.initEvent("TestEvent", true, true);
+ e.color = ["tuna"];
+ a.push(e);
+ }
+ garbage[garbageIndex++] = a;
+ if (garbageIndex == garbage.length)
+ garbageIndex = 0;
+ }
+};
+})());
diff --git a/js/src/devtools/gc-ubench/benchmarks/globalArrayArrayLiteral.js b/js/src/devtools/gc-ubench/benchmarks/globalArrayArrayLiteral.js
new file mode 100644
index 000000000..9982090d8
--- /dev/null
+++ b/js/src/devtools/gc-ubench/benchmarks/globalArrayArrayLiteral.js
@@ -0,0 +1,16 @@
+window.tests.set('globalArrayArrayLiteral', (function() {
+var garbage = [];
+var garbageIndex = 0;
+return {
+ description: "var foo = [[], ....]",
+ load: (N) => { garbage = new Array(N); },
+ unload: () => { garbage = []; garbageIndex = 0; },
+ makeGarbage: (N) => {
+ for (var i = 0; i < N; i++) {
+ garbage[garbageIndex++] = ['foo', 'bar', 'baz', 'baa'];
+ if (garbageIndex == garbage.length)
+ garbageIndex = 0;
+ }
+ }
+};
+})());
diff --git a/js/src/devtools/gc-ubench/benchmarks/globalArrayBuffer.js b/js/src/devtools/gc-ubench/benchmarks/globalArrayBuffer.js
new file mode 100644
index 000000000..4aee22ee7
--- /dev/null
+++ b/js/src/devtools/gc-ubench/benchmarks/globalArrayBuffer.js
@@ -0,0 +1,23 @@
+window.tests.set('globalArrayBuffer', (function() {
+var garbage = [];
+var garbageIndex = 0;
+return {
+ description: "var foo = ArrayBuffer(N); # (large malloc data)",
+
+ load: (N) => { garbage = new Array(N); },
+ unload: () => { garbage = []; garbageIndex = 0; },
+
+ defaultGarbageTotal: "8K",
+ defaultGarbagePerFrame: "4M",
+
+ makeGarbage: (N) => {
+ var ab = new ArrayBuffer(N);
+ var view = new Uint8Array(ab);
+ view[0] = 1;
+ view[N - 1] = 2;
+ garbage[garbageIndex++] = ab;
+ if (garbageIndex == garbage.length)
+ garbageIndex = 0;
+ }
+};
+})());
diff --git a/js/src/devtools/gc-ubench/benchmarks/globalArrayFgFinalized.js b/js/src/devtools/gc-ubench/benchmarks/globalArrayFgFinalized.js
new file mode 100644
index 000000000..f2f914376
--- /dev/null
+++ b/js/src/devtools/gc-ubench/benchmarks/globalArrayFgFinalized.js
@@ -0,0 +1,23 @@
+window.tests.set('globalArrayFgFinalized', (function() {
+var garbage = [];
+var garbageIndex = 0;
+return {
+ description: "var foo = [ new Map, new Map, ... ]; # (foreground finalized)",
+
+ load: (N) => { garbage = new Array(N); },
+ unload: () => { garbage = []; garbageIndex = 0; },
+
+ defaultGarbageTotal: "8K",
+ defaultGarbagePerFrame: "1M",
+
+ makeGarbage: (N) => {
+ var arr = [];
+ for (var i = 0; i < N; i++) {
+ arr.push(new Map);
+ }
+ garbage[garbageIndex++] = arr;
+ if (garbageIndex == garbage.length)
+ garbageIndex = 0;
+ }
+};
+})());
diff --git a/js/src/devtools/gc-ubench/benchmarks/globalArrayLargeArray.js b/js/src/devtools/gc-ubench/benchmarks/globalArrayLargeArray.js
new file mode 100644
index 000000000..2c0ff3564
--- /dev/null
+++ b/js/src/devtools/gc-ubench/benchmarks/globalArrayLargeArray.js
@@ -0,0 +1,18 @@
+window.tests.set('globalArrayLargeArray', (function() {
+var garbage = [];
+var garbageIndex = 0;
+return {
+ description: "var foo = [[...], ....]",
+ load: (N) => { garbage = new Array(N); },
+ unload: () => { garbage = []; garbageIndex = 0; },
+ makeGarbage: (N) => {
+ var a = new Array(N);
+ for (var i = 0; i < N; i++) {
+ a[i] = N - i;
+ }
+ garbage[garbageIndex++] = a;
+ if (garbageIndex == garbage.length)
+ garbageIndex = 0;
+ }
+};
+})());
diff --git a/js/src/devtools/gc-ubench/benchmarks/globalArrayLargeObject.js b/js/src/devtools/gc-ubench/benchmarks/globalArrayLargeObject.js
new file mode 100644
index 000000000..d676c255e
--- /dev/null
+++ b/js/src/devtools/gc-ubench/benchmarks/globalArrayLargeObject.js
@@ -0,0 +1,23 @@
+window.tests.set('globalArrayLargeObject', (function() {
+var garbage = [];
+var garbageIndex = 0;
+return {
+ description: "var foo = { LARGE }; # (large slots)",
+
+ load: (N) => { garbage = new Array(N); },
+ unload: () => { garbage = []; garbageIndex = 0; },
+
+ defaultGarbageTotal: "8K",
+ defaultGarbagePerFrame: "200K",
+
+ makeGarbage: (N) => {
+ var obj = {};
+ for (var i = 0; i < N; i++) {
+ obj["key" + i] = i;
+ }
+ garbage[garbageIndex++] = obj;
+ if (garbageIndex == garbage.length)
+ garbageIndex = 0;
+ }
+};
+})());
diff --git a/js/src/devtools/gc-ubench/benchmarks/globalArrayNewObject.js b/js/src/devtools/gc-ubench/benchmarks/globalArrayNewObject.js
new file mode 100644
index 000000000..2ab04dff0
--- /dev/null
+++ b/js/src/devtools/gc-ubench/benchmarks/globalArrayNewObject.js
@@ -0,0 +1,16 @@
+window.tests.set('globalArrayNewObject', (function() {
+var garbage = [];
+var garbageIndex = 0;
+return {
+ description: "var foo = [new Object(), ....]",
+ load: (N) => { garbage = new Array(N); },
+ unload: () => { garbage = []; garbageIndex = 0; },
+ makeGarbage: (N) => {
+ for (var i = 0; i < N; i++) {
+ garbage[garbageIndex++] = new Object();
+ if (garbageIndex == garbage.length)
+ garbageIndex = 0;
+ }
+ }
+};
+})());
diff --git a/js/src/devtools/gc-ubench/benchmarks/globalArrayObjectLiteral.js b/js/src/devtools/gc-ubench/benchmarks/globalArrayObjectLiteral.js
new file mode 100644
index 000000000..7a8c1b2d6
--- /dev/null
+++ b/js/src/devtools/gc-ubench/benchmarks/globalArrayObjectLiteral.js
@@ -0,0 +1,16 @@
+window.tests.set('globalArrayObjectLiteral', (function() {
+var garbage = [];
+var garbageIndex = 0;
+return {
+ description: "var foo = [{}, ....]",
+ load: (N) => { garbage = new Array(N); },
+ unload: () => { garbage = []; garbageIndex = 0; },
+ makeGarbage: (N) => {
+ for (var i = 0; i < N; i++) {
+ garbage[garbageIndex++] = {a: 'foo', b: 'bar', 0: 'foo', 1: 'bar'};
+ if (garbageIndex == garbage.length)
+ garbageIndex = 0;
+ }
+ }
+};
+})());
diff --git a/js/src/devtools/gc-ubench/benchmarks/globalArrayReallocArray.js b/js/src/devtools/gc-ubench/benchmarks/globalArrayReallocArray.js
new file mode 100644
index 000000000..6a7b16011
--- /dev/null
+++ b/js/src/devtools/gc-ubench/benchmarks/globalArrayReallocArray.js
@@ -0,0 +1,18 @@
+window.tests.set('globalArrayReallocArray', (function() {
+var garbage = [];
+var garbageIndex = 0;
+return {
+ description: "var foo = [[,,,], ....]",
+ load: (N) => { garbage = new Array(N); },
+ unload: () => { garbage = []; garbageIndex = 0; },
+ makeGarbage: (N) => {
+ var a = [];
+ for (var i = 0; i < N; i++) {
+ a[i] = N - i;
+ }
+ garbage[garbageIndex++] = a;
+ if (garbageIndex == garbage.length)
+ garbageIndex = 0;
+ }
+};
+})());
diff --git a/js/src/devtools/gc-ubench/benchmarks/largeArrayPropertyAndElements.js b/js/src/devtools/gc-ubench/benchmarks/largeArrayPropertyAndElements.js
new file mode 100644
index 000000000..36ae04971
--- /dev/null
+++ b/js/src/devtools/gc-ubench/benchmarks/largeArrayPropertyAndElements.js
@@ -0,0 +1,34 @@
+window.tests.set('largeArrayPropertyAndElements', (function() {
+ var garbage;
+ var index;
+
+ return {
+ description: "Large array with both properties and elements",
+
+ load: n => {
+ garbage = new Array(n);
+ garbage.fill(null);
+ index = 0;
+ },
+
+ unload: () => {
+ garbage = null;
+ index = 0;
+ },
+
+ defaultGarbageTotal: "100K",
+ defaultGarbagePerFrame: "30K",
+
+ makeGarbage: n => {
+ for (var i = 0; i < n; i++) {
+ index++;
+ index %= garbage.length;
+
+ var obj = {};
+ garbage[index] = obj;
+ garbage["key-" + index] = obj;
+ }
+ }
+ };
+
+}()));
diff --git a/js/src/devtools/gc-ubench/benchmarks/noAllocation.js b/js/src/devtools/gc-ubench/benchmarks/noAllocation.js
new file mode 100644
index 000000000..5e01c309b
--- /dev/null
+++ b/js/src/devtools/gc-ubench/benchmarks/noAllocation.js
@@ -0,0 +1,6 @@
+window.tests.set('noAllocation', {
+ description: "Do not generate any garbage.",
+ load: (N) => {},
+ unload: () => {},
+ makeGarbage: (N) => {}
+});
diff --git a/js/src/devtools/gc-ubench/benchmarks/pairCyclicWeakMap.js b/js/src/devtools/gc-ubench/benchmarks/pairCyclicWeakMap.js
new file mode 100644
index 000000000..4dcb4d57e
--- /dev/null
+++ b/js/src/devtools/gc-ubench/benchmarks/pairCyclicWeakMap.js
@@ -0,0 +1,33 @@
+window.tests.set('pairCyclicWeakMap', (function() {
+var garbage = [];
+var garbageIndex = 0;
+return {
+ description: "wm1[k1] = k2; wm2[k2] = k3; wm1[k3] = k4; wm2[k4] = ...",
+
+ defaultGarbagePerFrame: "1K",
+ defaultGarbageTotal: "1K",
+
+ load: (N) => { garbage = new Array(N); },
+
+ unload: () => { garbage = []; garbageIndex = 0; },
+
+ makeGarbage: (M) => {
+ var wm1 = new WeakMap();
+ var wm2 = new WeakMap();
+ var initialKey = {};
+ var key = initialKey;
+ var value = {};
+ for (var i = 0; i < M/2; i++) {
+ wm1.set(key, value);
+ key = value;
+ value = {};
+ wm2.set(key, value);
+ key = value;
+ value = {};
+ }
+ garbage[garbageIndex++] = [ initialKey, wm1, wm2 ];
+ if (garbageIndex == garbage.length)
+ garbageIndex = 0;
+ }
+};
+})());
diff --git a/js/src/devtools/gc-ubench/benchmarks/propertyTreeSplitting.js b/js/src/devtools/gc-ubench/benchmarks/propertyTreeSplitting.js
new file mode 100644
index 000000000..47001030f
--- /dev/null
+++ b/js/src/devtools/gc-ubench/benchmarks/propertyTreeSplitting.js
@@ -0,0 +1,25 @@
+window.tests.set('propertyTreeSplitting', (function() {
+var garbage = [];
+var garbageIndex = 0;
+return {
+ description: "use delete to generate Shape garbage",
+ load: (N) => { garbage = new Array(N); },
+ unload: () => { garbage = []; garbageIndex = 0; },
+ makeGarbage: (N) => {
+ function f()
+ {
+ var a1 = eval;
+ delete eval;
+ eval = a1;
+ var a3 = toString;
+ delete toString;
+ toString = a3;
+ }
+ for (var a = 0; a < N; ++a) {
+ garbage[garbageIndex++] = new f();
+ if (garbageIndex == garbage.length)
+ garbageIndex = 0;
+ }
+ }
+};
+})());
diff --git a/js/src/devtools/gc-ubench/benchmarks/selfCyclicWeakMap.js b/js/src/devtools/gc-ubench/benchmarks/selfCyclicWeakMap.js
new file mode 100644
index 000000000..0a5ffb084
--- /dev/null
+++ b/js/src/devtools/gc-ubench/benchmarks/selfCyclicWeakMap.js
@@ -0,0 +1,29 @@
+window.tests.set('selfCyclicWeakMap', (function() {
+var garbage = [];
+var garbageIndex = 0;
+return {
+ description: "var wm = new WeakMap(); wm[k1] = k2; wm[k2] = k3; ...",
+
+ defaultGarbagePerFrame: "1K",
+ defaultGarbageTotal: "1K",
+
+ load: (N) => { garbage = new Array(N); },
+
+ unload: () => { garbage = []; garbageIndex = 0; },
+
+ makeGarbage: (M) => {
+ var wm = new WeakMap();
+ var initialKey = {};
+ var key = initialKey;
+ var value = {};
+ for (var i = 0; i < M; i++) {
+ wm.set(key, value);
+ key = value;
+ value = {};
+ }
+ garbage[garbageIndex++] = [ initialKey, wm ];
+ if (garbageIndex == garbage.length)
+ garbageIndex = 0;
+ }
+};
+})());
diff --git a/js/src/devtools/gc-ubench/benchmarks/textNodes.js b/js/src/devtools/gc-ubench/benchmarks/textNodes.js
new file mode 100644
index 000000000..257e5f726
--- /dev/null
+++ b/js/src/devtools/gc-ubench/benchmarks/textNodes.js
@@ -0,0 +1,23 @@
+window.tests.set('textNodes', (function() {
+var garbage = [];
+var garbageIndex = 0;
+return {
+ description: "var foo = [ textNode, textNode, ... ]",
+
+ load: (N) => { garbage = new Array(N); },
+ unload: () => { garbage = []; garbageIndex = 0; },
+
+ defaultGarbagePerFrame: "100K",
+ defaultGarbageTotal: "8",
+
+ makeGarbage: (N) => {
+ var a = [];
+ for (var i = 0; i < N; i++) {
+ a.push(document.createTextNode("t" + i));
+ }
+ garbage[garbageIndex++] = a;
+ if (garbageIndex == garbage.length)
+ garbageIndex = 0;
+ }
+};
+})());
diff --git a/js/src/devtools/gc-ubench/harness.js b/js/src/devtools/gc-ubench/harness.js
new file mode 100644
index 000000000..f217ff1cb
--- /dev/null
+++ b/js/src/devtools/gc-ubench/harness.js
@@ -0,0 +1,661 @@
+// Per-frame time sampling infra. Also GC'd: hopefully will not perturb things too badly.
+var numSamples = 500;
+var delays = new Array(numSamples);
+var gcs = new Array(numSamples);
+var minorGCs = new Array(numSamples);
+var gcBytes = new Array(numSamples);
+var mallocBytes = new Array(numSamples);
+var sampleIndex = 0;
+var sampleTime = 16; // ms
+var gHistogram = new Map(); // {ms: count}
+
+var features = {
+ trackingSizes: ('mozMemory' in performance),
+ showingGCs: ('mozMemory' in performance),
+};
+
+// Draw state.
+var stopped = 0;
+var start;
+var prev;
+var latencyGraph;
+var memoryGraph;
+var ctx;
+var memoryCtx;
+
+// Current test state.
+var activeTest = undefined;
+var testDuration = undefined; // ms
+var testState = 'idle'; // One of 'idle' or 'running'.
+var testStart = undefined; // ms
+var testQueue = [];
+
+// Global defaults
+var globalDefaultGarbageTotal = "8M";
+var globalDefaultGarbagePerFrame = "8K";
+
+function Graph(ctx) {
+ this.ctx = ctx;
+
+ var { width, height } = ctx.canvas;
+ this.layout = {
+ xAxisLabel_Y: height - 20,
+ };
+}
+
+Graph.prototype.xpos = index => index * 2;
+
+Graph.prototype.clear = function () {
+ var { width, height } = this.ctx.canvas;
+ this.ctx.clearRect(0, 0, width, height);
+};
+
+Graph.prototype.drawScale = function (delay)
+{
+ this.drawHBar(delay, `${delay}ms`, 'rgb(150,150,150)');
+}
+
+Graph.prototype.draw60fps = function () {
+ this.drawHBar(1000/60, '60fps', '#00cf61', 25);
+}
+
+Graph.prototype.draw30fps = function () {
+ this.drawHBar(1000/30, '30fps', '#cf0061', 25);
+}
+
+Graph.prototype.drawAxisLabels = function (x_label, y_label)
+{
+ var ctx = this.ctx;
+ var { width, height } = ctx.canvas;
+
+ ctx.fillText(x_label, width / 2, this.layout.xAxisLabel_Y);
+
+ ctx.save();
+ ctx.rotate(Math.PI/2);
+ var start = height / 2 - ctx.measureText(y_label).width / 2;
+ ctx.fillText(y_label, start, -width+20);
+ ctx.restore();
+}
+
+Graph.prototype.drawFrame = function () {
+ var ctx = this.ctx;
+ var { width, height } = ctx.canvas;
+
+ // Draw frame to show size
+ ctx.strokeStyle = 'rgb(0,0,0)';
+ ctx.fillStyle = 'rgb(0,0,0)';
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(width, 0);
+ ctx.lineTo(width, height);
+ ctx.lineTo(0, height);
+ ctx.closePath();
+ ctx.stroke();
+}
+
+function LatencyGraph(ctx) {
+ Graph.call(this, ctx);
+ console.log(this.ctx);
+}
+
+LatencyGraph.prototype = Object.create(Graph.prototype);
+
+Object.defineProperty(LatencyGraph.prototype, 'constructor', {
+ enumerable: false,
+ value: LatencyGraph });
+
+LatencyGraph.prototype.ypos = function (delay) {
+ var { height } = this.ctx.canvas;
+
+ var r = height + 100 - Math.log(delay) * 64;
+ if (r < 5) return 5;
+ return r;
+}
+
+LatencyGraph.prototype.drawHBar = function (delay, label, color='rgb(0,0,0)', label_offset=0)
+{
+ var ctx = this.ctx;
+
+ ctx.fillStyle = color;
+ ctx.strokeStyle = color;
+ ctx.fillText(label, this.xpos(numSamples) + 4 + label_offset, this.ypos(delay) + 3);
+
+ ctx.beginPath();
+ ctx.moveTo(this.xpos(0), this.ypos(delay));
+ ctx.lineTo(this.xpos(numSamples) + label_offset, this.ypos(delay));
+ ctx.stroke();
+ ctx.strokeStyle = 'rgb(0,0,0)';
+ ctx.fillStyle = 'rgb(0,0,0)';
+}
+
+LatencyGraph.prototype.draw = function () {
+ var ctx = this.ctx;
+
+ this.clear();
+ this.drawFrame();
+
+ for (var delay of [ 10, 20, 30, 50, 100, 200, 400, 800 ])
+ this.drawScale(delay);
+ this.draw60fps();
+ this.draw30fps();
+
+ var worst = 0, worstpos = 0;
+ ctx.beginPath();
+ for (var i = 0; i < numSamples; i++) {
+ ctx.lineTo(this.xpos(i), this.ypos(delays[i]));
+ if (delays[i] >= worst) {
+ worst = delays[i];
+ worstpos = i;
+ }
+ }
+ ctx.stroke();
+
+ // Draw vertical lines marking minor and major GCs
+ if (features.showingGCs) {
+ var { width, height } = ctx.canvas;
+
+ ctx.strokeStyle = 'rgb(255,100,0)';
+ var idx = sampleIndex % numSamples;
+ var gcCount = gcs[idx];
+ for (var i = 0; i < numSamples; i++) {
+ idx = (sampleIndex + i) % numSamples;
+ if (gcCount < gcs[idx]) {
+ ctx.beginPath();
+ ctx.moveTo(this.xpos(idx), 0);
+ ctx.lineTo(this.xpos(idx), this.layout.xAxisLabel_Y);
+ ctx.stroke();
+ }
+ gcCount = gcs[idx];
+ }
+
+ ctx.strokeStyle = 'rgb(0,255,100)';
+ idx = sampleIndex % numSamples;
+ gcCount = gcs[idx];
+ for (var i = 0; i < numSamples; i++) {
+ idx = (sampleIndex + i) % numSamples;
+ if (gcCount < minorGCs[idx]) {
+ ctx.beginPath();
+ ctx.moveTo(this.xpos(idx), 0);
+ ctx.lineTo(this.xpos(idx), 20);
+ ctx.stroke();
+ }
+ gcCount = minorGCs[idx];
+ }
+ }
+
+ ctx.fillStyle = 'rgb(255,0,0)';
+ if (worst)
+ ctx.fillText(`${worst.toFixed(2)}ms`, this.xpos(worstpos) - 10, this.ypos(worst) - 14);
+
+ // Mark and label the slowest frame
+ ctx.beginPath();
+ var where = sampleIndex % numSamples;
+ ctx.arc(this.xpos(where), this.ypos(delays[where]), 5, 0, Math.PI*2, true);
+ ctx.fill();
+ ctx.fillStyle = 'rgb(0,0,0)';
+
+ this.drawAxisLabels('Time', 'Pause between frames (log scale)');
+}
+
+function MemoryGraph(ctx) {
+ Graph.call(this, ctx);
+ this.worstEver = this.bestEver = performance.mozMemory.zone.gcBytes;
+ this.limit = Math.max(this.worstEver, performance.mozMemory.zone.gcAllocTrigger);
+}
+
+MemoryGraph.prototype = Object.create(Graph.prototype);
+
+Object.defineProperty(MemoryGraph.prototype, 'constructor', {
+ enumerable: false,
+ value: MemoryGraph });
+
+MemoryGraph.prototype.ypos = function (size) {
+ var { height } = this.ctx.canvas;
+
+ var range = this.limit - this.bestEver;
+ var percent = (size - this.bestEver) / range;
+
+ return (1 - percent) * height * 0.9 + 20;
+}
+
+MemoryGraph.prototype.drawHBar = function (size, label, color='rgb(150,150,150)')
+{
+ var ctx = this.ctx;
+
+ var y = this.ypos(size);
+
+ ctx.fillStyle = color;
+ ctx.strokeStyle = color;
+ ctx.fillText(label, this.xpos(numSamples) + 4, y + 3);
+
+ ctx.beginPath();
+ ctx.moveTo(this.xpos(0), y);
+ ctx.lineTo(this.xpos(numSamples), y);
+ ctx.stroke();
+ ctx.strokeStyle = 'rgb(0,0,0)';
+ ctx.fillStyle = 'rgb(0,0,0)';
+}
+
+function format_gcBytes(bytes) {
+ if (bytes < 4000)
+ return `${bytes} bytes`;
+ else if (bytes < 4e6)
+ return `${(bytes / 1024).toFixed(2)} KB`;
+ else if (bytes < 4e9)
+ return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
+ else
+ return `${(bytes / 1024 / 1024 / 1024).toFixed(2)} GB`;
+};
+
+MemoryGraph.prototype.draw = function () {
+ var ctx = this.ctx;
+
+ this.clear();
+ this.drawFrame();
+
+ var worst = 0, worstpos = 0;
+ for (var i = 0; i < numSamples; i++) {
+ if (gcBytes[i] >= worst) {
+ worst = gcBytes[i];
+ worstpos = i;
+ }
+ if (gcBytes[i] < this.bestEver) {
+ this.bestEver = gcBytes[i];
+ }
+ }
+
+ if (this.worstEver < worst) {
+ this.worstEver = worst;
+ this.limit = Math.max(this.worstEver, performance.mozMemory.zone.gcAllocTrigger);
+ }
+
+ this.drawHBar(this.bestEver, `${format_gcBytes(this.bestEver)} min`, '#00cf61');
+ this.drawHBar(this.worstEver, `${format_gcBytes(this.worstEver)} max`, '#cc1111');
+ this.drawHBar(performance.mozMemory.zone.gcAllocTrigger, `${format_gcBytes(performance.mozMemory.zone.gcAllocTrigger)} trigger`, '#cc11cc');
+
+ ctx.fillStyle = 'rgb(255,0,0)';
+ if (worst)
+ ctx.fillText(format_gcBytes(worst), this.xpos(worstpos) - 10, this.ypos(worst) - 14);
+
+ ctx.beginPath();
+ var where = sampleIndex % numSamples;
+ ctx.arc(this.xpos(where), this.ypos(gcBytes[where]), 5, 0, Math.PI*2, true);
+ ctx.fill();
+
+ ctx.beginPath();
+ for (var i = 0; i < numSamples; i++) {
+ if (i == (sampleIndex + 1) % numSamples)
+ ctx.moveTo(this.xpos(i), this.ypos(gcBytes[i]));
+ else
+ ctx.lineTo(this.xpos(i), this.ypos(gcBytes[i]));
+ if (i == where)
+ ctx.stroke();
+ }
+ ctx.stroke();
+
+ this.drawAxisLabels('Time', 'Heap Memory Usage');
+}
+
+function stopstart()
+{
+ if (stopped) {
+ window.requestAnimationFrame(handler);
+ prev = performance.now();
+ start += prev - stopped;
+ document.getElementById('stop').value = 'Pause';
+ stopped = 0;
+ } else {
+ document.getElementById('stop').value = 'Resume';
+ stopped = performance.now();
+ }
+}
+
+var previous = 0;
+function handler(timestamp)
+{
+ if (stopped)
+ return;
+
+ if (testState === 'running' && (timestamp - testStart) > testDuration)
+ end_test(timestamp);
+
+ if (testState == 'running')
+ document.getElementById("test-progress").textContent = ((testDuration - (timestamp - testStart))/1000).toFixed(1) + " sec";
+
+ activeTest.makeGarbage(activeTest.garbagePerFrame);
+
+ var elt = document.getElementById('data');
+ var delay = timestamp - prev;
+ prev = timestamp;
+
+ // Take the histogram at 10us intervals so that we have enough resolution to capture.
+ // a 16.66[666] target with adequate accuracy.
+ update_histogram(gHistogram, Math.round(delay * 100));
+
+ var t = timestamp - start;
+ var newIndex = Math.round(t / sampleTime);
+ while (sampleIndex < newIndex) {
+ sampleIndex++;
+ var idx = sampleIndex % numSamples;
+ delays[idx] = delay;
+ if (features.trackingSizes)
+ gcBytes[idx] = performance.mozMemory.gcBytes;
+ if (features.showingGCs) {
+ gcs[idx] = performance.mozMemory.gcNumber;
+ minorGCs[idx] = performance.mozMemory.minorGCCount;
+ }
+ }
+
+ latencyGraph.draw();
+ if (memoryGraph)
+ memoryGraph.draw();
+ window.requestAnimationFrame(handler);
+}
+
+function summarize(arr) {
+ if (arr.length == 0)
+ return [];
+
+ var result = [];
+ var run_start = 0;
+ var prev = arr[0];
+ for (var i = 1; i <= arr.length; i++) {
+ if (i == arr.length || arr[i] != prev) {
+ if (i == run_start + 1) {
+ result.push(arr[i]);
+ } else {
+ result.push(prev + " x " + (i - run_start));
+ }
+ run_start = i;
+ }
+ if (i != arr.length)
+ prev = arr[i];
+ }
+
+ return result;
+}
+
+function update_histogram(histogram, delay)
+{
+ var current = histogram.has(delay) ? histogram.get(delay) : 0;
+ histogram.set(delay, ++current);
+}
+
+function reset_draw_state()
+{
+ for (var i = 0; i < numSamples; i++)
+ delays[i] = 0;
+ start = prev = performance.now();
+ sampleIndex = 0;
+}
+
+function onunload()
+{
+ if (activeTest)
+ activeTest.unload();
+ activeTest = undefined;
+}
+
+function onload()
+{
+ // Load initial test duration.
+ duration_changed();
+
+ // Load initial garbage size.
+ garbage_total_changed();
+ garbage_per_frame_changed();
+
+ // Populate the test selection dropdown.
+ var select = document.getElementById("test-selection");
+ for (var [name, test] of tests) {
+ test.name = name;
+ var option = document.createElement("option");
+ option.id = name;
+ option.text = name;
+ option.title = test.description;
+ select.add(option);
+ }
+
+ // Load the initial test.
+ change_active_test('noAllocation');
+
+ // Polyfill rAF.
+ var requestAnimationFrame =
+ window.requestAnimationFrame || window.mozRequestAnimationFrame ||
+ window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
+ window.requestAnimationFrame = requestAnimationFrame;
+
+ // Acquire our canvas.
+ var canvas = document.getElementById('graph');
+ latencyGraph = new LatencyGraph(canvas.getContext('2d'));
+
+ if (!performance.mozMemory) {
+ document.getElementById('memgraph-disabled').style.display = 'block';
+ document.getElementById('track-sizes-div').style.display = 'none';
+ }
+
+ trackHeapSizes(document.getElementById('track-sizes').checked);
+
+ // Start drawing.
+ reset_draw_state();
+ window.requestAnimationFrame(handler);
+}
+
+function run_one_test()
+{
+ start_test_cycle([activeTest.name]);
+}
+
+function run_all_tests()
+{
+ start_test_cycle(tests.keys());
+}
+
+function start_test_cycle(tests_to_run)
+{
+ // Convert from an iterable to an array for pop.
+ testQueue = [];
+ for (var key of tests_to_run)
+ testQueue.push(key);
+ testState = 'running';
+ testStart = performance.now();
+ gHistogram.clear();
+
+ start_test(testQueue.shift());
+ reset_draw_state();
+}
+
+function start_test(testName)
+{
+ change_active_test(testName);
+ console.log(`Running test: ${testName}`);
+ document.getElementById("test-selection").value = testName;
+}
+
+function end_test(timestamp)
+{
+ document.getElementById("test-progress").textContent = "(not running)";
+ report_test_result(activeTest, gHistogram);
+ gHistogram.clear();
+ console.log(`Ending test ${activeTest.name}`);
+ if (testQueue.length) {
+ start_test(testQueue.shift());
+ testStart = timestamp;
+ } else {
+ testState = 'idle';
+ testStart = 0;
+ }
+ reset_draw_state();
+}
+
+function report_test_result(test, histogram)
+{
+ var resultList = document.getElementById('results-display');
+ var resultElem = document.createElement("div");
+ var score = compute_test_score(histogram);
+ var sparks = compute_test_spark_histogram(histogram);
+ var params = `(${format_units(test.garbagePerFrame)},${format_units(test.garbageTotal)})`;
+ resultElem.innerHTML = `${score.toFixed(3)} ms/s : ${sparks} : ${test.name}${params} - ${test.description}`;
+ resultList.appendChild(resultElem);
+}
+
+// Compute a score based on the total ms we missed frames by per second.
+function compute_test_score(histogram)
+{
+ var score = 0;
+ for (var [delay, count] of histogram) {
+ delay = delay / 100;
+ score += Math.abs((delay - 16.66) * count);
+ }
+ score = score / (testDuration / 1000);
+ return Math.round(score * 1000) / 1000;
+}
+
+// Build a spark-lines histogram for the test results to show with the aggregate score.
+function compute_test_spark_histogram(histogram)
+{
+ var ranges = [
+ [-99999999, 16.6],
+ [16.6, 16.8],
+ [16.8, 25],
+ [25, 33.4],
+ [33.4, 60],
+ [60, 100],
+ [100, 300],
+ [300, 99999999],
+ ];
+ var rescaled = new Map();
+ for (var [delay, count] of histogram) {
+ delay = delay / 100;
+ for (var i = 0; i < ranges.length; ++i) {
+ var low = ranges[i][0];
+ var high = ranges[i][1];
+ if (low <= delay && delay < high) {
+ update_histogram(rescaled, i);
+ break;
+ }
+ }
+ }
+ var total = 0;
+ for (var [i, count] of rescaled)
+ total += count;
+ var sparks = "▁▂▃▄▅▆▇█";
+ var colors = ['#aaaa00', '#007700', '#dd0000', '#ff0000',
+ '#ff0000', '#ff0000', '#ff0000', '#ff0000'];
+ var line = "";
+ for (var i = 0; i < ranges.length; ++i) {
+ var amt = rescaled.has(i) ? rescaled.get(i) : 0;
+ var spark = sparks.charAt(parseInt(amt/total*8));
+ line += `<span style="color:${colors[i]}">${spark}</span>`;
+ }
+ return line;
+}
+
+function reload_active_test()
+{
+ activeTest.unload();
+ activeTest.load(activeTest.garbageTotal);
+}
+
+function change_active_test(new_test_name)
+{
+ if (activeTest)
+ activeTest.unload();
+ activeTest = tests.get(new_test_name);
+
+ if (!activeTest.garbagePerFrame)
+ activeTest.garbagePerFrame = parse_units(activeTest.defaultGarbagePerFrame || globalDefaultGarbagePerFrame);
+ if (!activeTest.garbageTotal)
+ activeTest.garbageTotal = parse_units(activeTest.defaultGarbageTotal || globalDefaultGarbageTotal);
+
+ document.getElementById("garbage-per-frame").value = format_units(activeTest.garbagePerFrame);
+ document.getElementById("garbage-total").value = format_units(activeTest.garbageTotal);
+
+ activeTest.load(activeTest.garbageTotal);
+}
+
+function duration_changed()
+{
+ var durationInput = document.getElementById('test-duration');
+ testDuration = parseInt(durationInput.value) * 1000;
+ console.log(`Updated test duration to: ${testDuration / 1000} seconds`);
+}
+
+function test_changed()
+{
+ var select = document.getElementById("test-selection");
+ console.log(`Switching to test: ${select.value}`);
+ change_active_test(select.value);
+ gHistogram.clear();
+ reset_draw_state();
+}
+
+function parse_units(v)
+{
+ if (v.length == 0)
+ return NaN;
+ var lastChar = v[v.length - 1].toLowerCase();
+ if (!isNaN(parseFloat(lastChar)))
+ return parseFloat(v);
+ var units = parseFloat(v.substr(0, v.length - 1));
+ if (lastChar == "k")
+ return units * 1e3;
+ if (lastChar == "m")
+ return units * 1e6;
+ if (lastChar == "g")
+ return units * 1e9;
+ return NaN;
+}
+
+function format_units(n)
+{
+ n = String(n);
+ if (n.length > 9 && n.substr(-9) == "000000000")
+ return n.substr(0, n.length - 9) + "G";
+ else if (n.length > 9 && n.substr(-6) == "000000")
+ return n.substr(0, n.length - 6) + "M";
+ else if (n.length > 3 && n.substr(-3) == "000")
+ return n.substr(0, n.length - 3) + "K";
+ else
+ return String(n);
+}
+
+function garbage_total_changed()
+{
+ var value = parse_units(document.getElementById('garbage-total').value);
+ if (isNaN(value))
+ return;
+ if (activeTest) {
+ activeTest.garbageTotal = value;
+ console.log(`Updated garbage-total to ${activeTest.garbageTotal} items`);
+ reload_active_test();
+ }
+ gHistogram.clear();
+ reset_draw_state();
+}
+
+function garbage_per_frame_changed()
+{
+ var value = parse_units(document.getElementById('garbage-per-frame').value);
+ if (isNaN(value))
+ return;
+ if (activeTest) {
+ activeTest.garbagePerFrame = value;
+ console.log(`Updated garbage-per-frame to ${activeTest.garbagePerFrame} items`);
+ }
+}
+
+function trackHeapSizes(track)
+{
+ features.trackingSizes = track;
+
+ var canvas = document.getElementById('memgraph');
+
+ if (features.trackingSizes) {
+ canvas.style.display = 'block';
+ memoryGraph = new MemoryGraph(canvas.getContext('2d'));
+ } else {
+ canvas.style.display = 'none';
+ memoryGraph = null;
+ }
+}
diff --git a/js/src/devtools/gc-ubench/index.html b/js/src/devtools/gc-ubench/index.html
new file mode 100644
index 000000000..d5ab2a083
--- /dev/null
+++ b/js/src/devtools/gc-ubench/index.html
@@ -0,0 +1,84 @@
+<html>
+<head>
+ <title>GC uBench</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+
+ <!-- Include benchmark modules. -->
+ <script>var tests = new Map();</script>
+ <script src="benchmarks/noAllocation.js"></script>
+ <script src="benchmarks/globalArrayNewObject.js"></script>
+ <script src="benchmarks/globalArrayArrayLiteral.js"></script>
+ <script src="benchmarks/globalArrayLargeArray.js"></script>
+ <script src="benchmarks/globalArrayLargeObject.js"></script>
+ <script src="benchmarks/globalArrayObjectLiteral.js"></script>
+ <script src="benchmarks/globalArrayReallocArray.js"></script>
+ <script src="benchmarks/globalArrayBuffer.js"></script>
+ <script src="benchmarks/globalArrayFgFinalized.js"></script>
+ <script src="benchmarks/largeArrayPropertyAndElements.js"></script>
+ <script src="benchmarks/selfCyclicWeakMap.js"></script>
+ <script src="benchmarks/pairCyclicWeakMap.js"></script>
+ <script src="benchmarks/textNodes.js"></script>
+ <script src="benchmarks/bigTextNodes.js"></script>
+ <script src="benchmarks/events.js"></script>
+ <script src="benchmarks/expandoEvents.js"></script>
+ <script src="benchmarks/propertyTreeSplitting.js"></script>
+
+ <script src="harness.js"></script>
+
+</head>
+
+<body onload="onload()" onunload="onunload()">
+
+<canvas id="graph" width="1080" height="400" style="padding-left:10px"></canvas>
+<canvas id="memgraph" width="1080" height="400" style="padding-left:10px"></canvas>
+<div id="memgraph-disabled" style="display: none"><i>No performance.mozMemory object available. Set dom.enable_memory_stats to True to see heap size info.</i></div>
+
+<hr>
+
+<div id='track-sizes-div'>
+ Show heap size graph: <input id='track-sizes' type='checkbox' onclick="trackHeapSizes(this.checked)">
+</div>
+
+<div>
+ <input type="button" id="stop" value="Pause" onclick="stopstart()"></input>
+</div>
+
+<div>
+ Duration: <input type="text" id="test-duration" size="3" value="8" onchange="duration_changed()"></input>s
+ <input type="button" id="test-one" value="Run Test" onclick="run_one_test()"></input>
+ <input type="button" id="test-all" value="Run All Tests" onclick="run_all_tests()"></input>
+</div>
+
+<div>
+ Currently running test load:
+ <select id="test-selection" required onchange="test_changed()"></select>
+</div>
+
+<div>
+ &nbsp;&nbsp;&nbsp;&nbsp;Time remaining: <span id="test-progress">(not running)</span>
+</div
+
+<div>
+ &nbsp;&nbsp;&nbsp;&nbsp;60 fps: <span id="pct60">n/a</span>
+ &nbsp;&nbsp;&nbsp;&nbsp;45 fps: <span id="pct45">n/a</span>
+ &nbsp;&nbsp;&nbsp;&nbsp;30 fps: <span id="pct30">n/a</span>
+</div
+
+<div>
+ &nbsp;&nbsp;&nbsp;&nbsp;Garbage items per frame:
+ <input type="text" id="garbage-per-frame" size="5" value="8K"
+ onchange="garbage_per_frame_changed()"></input>
+</div>
+<div>
+ &nbsp;&nbsp;&nbsp;&nbsp;Garbage piles:
+ <input type="text" id="garbage-total" size="5" value="8M"
+ onchange="garbage_total_changed()"></input>
+</div>
+
+<div id="results-Area">
+ Test Results:
+ <div id="results-display" style="padding-left: 10px; border: 1px solid black;"></div>
+</div>
+
+</body>
+</html>