summaryrefslogtreecommitdiffstats
path: root/js/src/devtools/gc-ubench
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/devtools/gc-ubench')
-rw-r--r--js/src/devtools/gc-ubench/harness.js11
-rw-r--r--js/src/devtools/gc-ubench/index.html70
-rw-r--r--js/src/devtools/gc-ubench/ui.js180
3 files changed, 156 insertions, 105 deletions
diff --git a/js/src/devtools/gc-ubench/harness.js b/js/src/devtools/gc-ubench/harness.js
index db7fa06d63..124baa17a5 100644
--- a/js/src/devtools/gc-ubench/harness.js
+++ b/js/src/devtools/gc-ubench/harness.js
@@ -254,14 +254,19 @@ var AllocationLoadManager = class {
var gLoadMgr = undefined;
function format_with_units(n, label, shortlabel, kbase) {
+ function format(n, prefix, unit) {
+ let s = Number.isInteger(n) ? n.toString() : n.toFixed(2);
+ return `${s}${prefix}${unit}`;
+ }
+
if (n < kbase * 4) {
return `${n} ${label}`;
} else if (n < kbase ** 2 * 4) {
- return `${(n / kbase).toFixed(2)}K${shortlabel}`;
+ return format(n / kbase, 'K', shortlabel);
} else if (n < kbase ** 3 * 4) {
- return `${(n / kbase ** 2).toFixed(2)}M${shortlabel}`;
+ return format(n / kbase ** 2, 'M', shortlabel);
}
- return `${(n / kbase ** 3).toFixed(2)}G${shortlabel}`;
+ return format(n / kbase ** 3, 'G', shortlabel);
}
function format_bytes(bytes) {
diff --git a/js/src/devtools/gc-ubench/index.html b/js/src/devtools/gc-ubench/index.html
index 4abce385f2..4efbb75def 100644
--- a/js/src/devtools/gc-ubench/index.html
+++ b/js/src/devtools/gc-ubench/index.html
@@ -15,61 +15,59 @@
<!-- List of garbage-creating test loads -->
<script src="test_list.js"></script>
-
- <!-- Collect all test loads into a `tests` Map -->
- <script>
- var tests = new Map();
- foreach_test_file(path => import("./" + path));
- </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>
+<canvas id="graph" width="980" height="320" style="padding-left:10px"></canvas>
+<canvas id="memgraph" width="980" height="320" style="padding-left:10px"></canvas>
<div id="memgraph-disabled" style="display: none"><i>No performance.mozMemory object available. If running Firefox, 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>
+<form>
+ <div id='track-sizes-div'>
+ <input id='track-sizes' type='checkbox' onclick="trackHeapSizes(this.checked)">
+ <label for='track-sizes'>Show heap size graph</label>
+ </div>
-<div>
- Update display:
- <input type="checkbox" id="do-graph" onchange="onUpdateDisplayChanged()" checked></input>
-</div>
+ <div>
+ <input type="checkbox" id="do-graph" onchange="onUpdateDisplayChanged()" checked></input>
+ <label for='do-graph'>Update display</label>
+ </div>
-<div>
- Run allocation load
- <input type="checkbox" id="do-load" onchange="onDoLoadChange()" checked></input>
-</div>
+ <div>
+ <input type="checkbox" id="do-load" onchange="onDoLoadChange()" checked></input>
+ <label for='do-load'>Run allocation load</label>
+ </div>
-<div>
- Allocation load:
+ <div>
+ <label for='test-selection'>Allocation load:</label>
<select id="test-selection" required onchange="onLoadChange()"></select>
<span id="load-running">(init)</span>
-</div>
+ </div>
-<div>
- &nbsp;&nbsp;&nbsp;&nbsp;Garbage items per frame:
- <input type="text" id="garbage-per-frame" size="5" value="8K"
+ <div>
+ <label for='garbage-per-frame'>&nbsp;&nbsp;&nbsp;&nbsp;Garbage items per frame:</label>
+ <input type="text" id="garbage-per-frame" size="8" value="8K"
onchange="garbage_per_frame_changed()"></input>
-</div>
-<div>
- &nbsp;&nbsp;&nbsp;&nbsp;Garbage piles:
- <input type="text" id="garbage-piles" size="5" value="8"
+ </div>
+
+ <div>
+ <label for='garbage-piles'>&nbsp;&nbsp;&nbsp;&nbsp;Garbage piles:</label>
+ <input type="text" id="garbage-piles" size="8" value="8"
onchange="garbage_piles_changed()"></input>
-</div>
+ </div>
+</form>
<hr>
-<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>
+<form>
+ <label for='test-duration'>Duration:</label>
+ <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>
+</form>
<div>
&nbsp;&nbsp;&nbsp;&nbsp;Time remaining: <span id="test-progress">(not running)</span>
diff --git a/js/src/devtools/gc-ubench/ui.js b/js/src/devtools/gc-ubench/ui.js
index 4905f97904..4ff84481c9 100644
--- a/js/src/devtools/gc-ubench/ui.js
+++ b/js/src/devtools/gc-ubench/ui.js
@@ -10,6 +10,8 @@ var stroke = {
var numSamples = 500;
+var tests = new Map();
+
var gHistogram = new Map(); // {ms: count}
var gHistory = new FrameHistory(numSamples);
var gPerf = new PerfTracker();
@@ -51,9 +53,15 @@ var Firefox = class extends Host {
get gcBytes() {
return gMemory.zone.gcBytes;
}
+ get mallocBytes() {
+ return gMemory.zone.mallocBytes;
+ }
get gcAllocTrigger() {
return gMemory.zone.gcAllocTrigger;
}
+ get mallocTrigger() {
+ return gMemory.zone.mallocTriggerBytes;
+ }
features = {
haveMemorySizes: 'gcBytes' in gMemory,
@@ -85,22 +93,32 @@ function parse_units(v) {
}
var Graph = class {
- constructor(ctx) {
- this.ctx = ctx;
+ constructor(canvas) {
+ this.ctx = canvas.getContext('2d');
+
+ // Adjust scale for high-DPI displays.
+ this.scale = window.devicePixelRatio || 1;
+ let rect = canvas.getBoundingClientRect();
+ canvas.width = Math.floor(rect.width * this.scale);
+ canvas.height = Math.floor(rect.height * this.scale);
+ canvas.style.width = rect.width;
+ canvas.style.height = rect.height;
+
+ // Record canvas size to draw into.
+ this.width = canvas.width;
+ this.height = canvas.height;
- var { height } = ctx.canvas;
this.layout = {
- xAxisLabel_Y: height - 20,
+ xAxisLabel_Y: this.height - 20 * this.scale,
};
}
xpos(index) {
- return index * 2;
+ return (index / numSamples) * (this.width - 100 * this.scale);
}
clear() {
- const { width, height } = this.ctx.canvas;
- this.ctx.clearRect(0, 0, width, height);
+ this.ctx.clearRect(0, 0, this.width, this.height);
}
drawScale(delay) {
@@ -117,20 +135,22 @@ var Graph = class {
drawAxisLabels(x_label, y_label) {
const ctx = this.ctx;
- const { width, height } = ctx.canvas;
- ctx.fillText(x_label, width / 2, this.layout.xAxisLabel_Y);
+ ctx.font = `${10 * this.scale}px sans-serif`;
+
+ ctx.fillText(x_label, this.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);
+ var start = this.height / 2 - ctx.measureText(y_label).width / 2;
+ ctx.fillText(y_label, start, -this.width + 20 * this.scale);
ctx.restore();
}
drawFrame() {
const ctx = this.ctx;
- const { width, height } = ctx.canvas;
+ const width = this.width;
+ const height = this.height;
// Draw frame to show size
ctx.strokeStyle = "rgb(0,0,0)";
@@ -148,22 +168,17 @@ var Graph = class {
var LatencyGraph = class extends Graph {
constructor(ctx) {
super(ctx);
- console.log(this.ctx);
}
ypos(delay) {
- const { height } = this.ctx.canvas;
-
- const r = height + 100 - Math.log(delay) * 64;
- if (r < 5) {
- return 5;
- }
- return r;
+ return this.height + this.scale * (100 - Math.log(delay) * 64);
}
drawHBar(delay, label, color = "rgb(0,0,0)", label_offset = 0) {
const ctx = this.ctx;
+ let y = this.ypos(delay);
+
ctx.fillStyle = color;
ctx.strokeStyle = color;
ctx.fillText(
@@ -277,20 +292,19 @@ var LatencyGraph = class extends Graph {
var MemoryGraph = class extends Graph {
constructor(ctx) {
super(ctx);
- this.worstEver = this.bestEver = gHost.gcBytes;
- this.limit = Math.max(this.worstEver, gHost.gcAllocTrigger);
+ this.range = 1;
}
ypos(size) {
- const { height } = this.ctx.canvas;
-
- const range = this.limit - this.bestEver;
- const percent = (size - this.bestEver) / range;
+ const percent = size / this.range;
+ return (1 - percent) * this.height * 0.9 + this.scale * 20;
+ }
- return (1 - percent) * height * 0.9 + 20;
+ drawHBarForBytes(size, name, color) {
+ this.drawHBar(size, `${format_bytes(size)} ${name}`, color)
}
- drawHBar(size, label, color = "rgb(150,150,150)") {
+ drawHBar(size, label, color) {
const ctx = this.ctx;
const y = this.ypos(size);
@@ -313,50 +327,48 @@ var MemoryGraph = class extends Graph {
this.clear();
this.drawFrame();
- var worst = 0,
- worstpos = 0;
+ let gcMaxPos = 0;
+ let mallocMaxPos = 0;
+ let gcMax = 0;
+ let mallocMax = 0;
for (let i = 0; i < numSamples; i++) {
- if (gHistory.gcBytes[i] >= worst) {
- worst = gHistory.gcBytes[i];
- worstpos = i;
+ if (gHistory.gcBytes[i] >= gcMax) {
+ gcMax = gHistory.gcBytes[i];
+ gcMaxPos = i;
}
- if (gHistory.gcBytes[i] < this.bestEver) {
- this.bestEver = gHistory.gcBytes[i];
+ if (gHistory.mallocBytes[i] >= mallocMax) {
+ mallocMax = gHistory.mallocBytes[i];
+ mallocMaxPos = i;
}
}
- if (this.worstEver < worst) {
- this.worstEver = worst;
- this.limit = Math.max(this.worstEver, gHost.gcAllocTrigger);
- }
+ this.range = Math.max(gcMax, mallocMax, gHost.gcAllocTrigger, gHost.mallocTrigger);
- this.drawHBar(
- this.bestEver,
- `${format_bytes(this.bestEver)} min`,
- "#00cf61"
- );
- this.drawHBar(
- this.worstEver,
- `${format_bytes(this.worstEver)} max`,
- "#cc1111"
- );
- this.drawHBar(
- gHost.gcAllocTrigger,
- `${format_bytes(gHost.gcAllocTrigger)} trigger`,
- "#cc11cc"
- );
+ this.drawHBarForBytes(gcMax, "GC max", "#00cf61");
+ this.drawHBarForBytes(mallocMax, "Malloc max", "#cc1111");
+ this.drawHBarForBytes(gHost.gcAllocTrigger, "GC trigger", "#cc11cc");
+ this.drawHBarForBytes(gHost.mallocTrigger, "Malloc trigger", "#cc11cc");
ctx.fillStyle = "rgb(255,0,0)";
- if (worst) {
+
+ if (gcMax !== 0) {
ctx.fillText(
- format_bytes(worst),
- this.xpos(worstpos) - 10,
- this.ypos(worst) - 14
+ format_bytes(gcMax),
+ this.xpos(gcMaxPos) - 10,
+ this.ypos(gcMax) - 14
+ );
+ }
+ if (mallocMax !== 0) {
+ ctx.fillText(
+ format_bytes(mallocMax),
+ this.xpos(mallocMaxPos) - 10,
+ this.ypos(mallocMax) - 14
);
}
+ const where = sampleIndex % numSamples;
+
ctx.beginPath();
- var where = sampleIndex % numSamples;
ctx.arc(
this.xpos(where),
this.ypos(gHistory.gcBytes[where]),
@@ -366,13 +378,40 @@ var MemoryGraph = class extends Graph {
true
);
ctx.fill();
+ ctx.beginPath();
+ ctx.arc(
+ this.xpos(where),
+ this.ypos(gHistory.mallocBytes[where]),
+ 5,
+ 0,
+ Math.PI * 2,
+ true
+ );
+ ctx.fill();
+
+ ctx.beginPath();
+ for (let i = 0; i < numSamples; i++) {
+ let x = this.xpos(i);
+ let y = this.ypos(gHistory.gcBytes[i]);
+ if (i == (sampleIndex + 1) % numSamples) {
+ ctx.moveTo(x, y);
+ } else {
+ ctx.lineTo(x, y);
+ }
+ if (i == where) {
+ ctx.stroke();
+ }
+ }
+ ctx.stroke();
ctx.beginPath();
for (let i = 0; i < numSamples; i++) {
+ let x = this.xpos(i);
+ let y = this.ypos(gHistory.mallocBytes[i]);
if (i == (sampleIndex + 1) % numSamples) {
- ctx.moveTo(this.xpos(i), this.ypos(gHistory.gcBytes[i]));
+ ctx.moveTo(x, y);
} else {
- ctx.lineTo(this.xpos(i), this.ypos(gHistory.gcBytes[i]));
+ ctx.lineTo(x, y);
}
if (i == where) {
ctx.stroke();
@@ -380,6 +419,8 @@ var MemoryGraph = class extends Graph {
}
ctx.stroke();
+ ctx.fillStyle = "rgb(0,0,0)";
+
this.drawAxisLabels("Time", "Heap Memory Usage");
}
};
@@ -466,10 +507,17 @@ function reset_draw_state() {
}
function onunload() {
- gLoadMgr.deactivateLoad();
+ if (gLoadMgr) {
+ gLoadMgr.deactivateLoad();
+ }
}
-function onload() {
+async function onload() {
+ // Collect all test loads into the `tests` Map.
+ let imports = [];
+ foreach_test_file(path => imports.push(import("./" + path)));
+ await Promise.all(imports);
+
// The order of `tests` is currently based on their asynchronous load
// order, rather than the listed order. Rearrange by extracting the test
// names from their filenames, which is kind of gross.
@@ -517,7 +565,7 @@ function onload() {
// Acquire our canvas.
var canvas = document.getElementById("graph");
- latencyGraph = new LatencyGraph(canvas.getContext("2d"));
+ latencyGraph = new LatencyGraph(canvas);
if (!gHost.features.haveMemorySizes) {
document.getElementById("memgraph-disabled").style.display = "block";
@@ -676,7 +724,7 @@ function garbage_per_frame_changed() {
return;
}
if (gLoadMgr.load_running()) {
- gLoadMgr.change_garbagePerFrame = value;
+ gLoadMgr.change_garbagePerFrame(value);
console.log(
`Updated garbage-per-frame to ${
gLoadMgr.activeLoad().garbagePerFrame
@@ -692,7 +740,7 @@ function trackHeapSizes(track) {
if (enabled.trackingSizes) {
canvas.style.display = "block";
- memoryGraph = new MemoryGraph(canvas.getContext("2d"));
+ memoryGraph = new MemoryGraph(canvas);
} else {
canvas.style.display = "none";
memoryGraph = null;