diff options
Diffstat (limited to 'js/src/devtools/gc-ubench')
-rw-r--r-- | js/src/devtools/gc-ubench/harness.js | 11 | ||||
-rw-r--r-- | js/src/devtools/gc-ubench/index.html | 70 | ||||
-rw-r--r-- | js/src/devtools/gc-ubench/ui.js | 180 |
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> - Garbage items per frame: - <input type="text" id="garbage-per-frame" size="5" value="8K" + <div> + <label for='garbage-per-frame'> Garbage items per frame:</label> + <input type="text" id="garbage-per-frame" size="8" value="8K" onchange="garbage_per_frame_changed()"></input> -</div> -<div> - Garbage piles: - <input type="text" id="garbage-piles" size="5" value="8" + </div> + + <div> + <label for='garbage-piles'> 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> 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; |