diff --git a/js/src/devtools/gc-ubench/harness.js b/js/src/devtools/gc-ubench/harness.js index 7d7a64dc92f5..c522ddce1e4e 100644 --- a/js/src/devtools/gc-ubench/harness.js +++ b/js/src/devtools/gc-ubench/harness.js @@ -5,6 +5,7 @@ // Global defaults var gDefaultGarbagePiles = "8M"; var gDefaultGarbagePerFrame = "8K"; +var gDefaultTestDuration = 8.0; function parse_units(v) { if (!v.length) { @@ -81,6 +82,47 @@ class AllocationLoad { } } +class LoadCycle { + constructor(tests_to_run, duration) { + this.queue = [...tests_to_run]; + this.duration = duration; + this.idx = -1; + } + + get current() { + return this.queue[this.idx]; + } + + start(now = performance.now()) { + this.idx = 0; + this.cycleStart = this.started = now; + } + + tick(now = performance.now()) { + if (this.currentLoadElapsed(now) < this.duration) { + return; + } + + this.idx++; + this.started = now; + if (this.idx >= this.queue.length) { + this.idx = -1; + } + } + + done() { + return this.idx == -1; + } + + cycleElapsed(now = performance.now()) { + return now - this.cycleStart; + } + + currentLoadElapsed(now = performance.now()) { + return now - this.started; + } +} + class AllocationLoadManager { constructor(tests) { this._loads = new Map(); @@ -89,6 +131,17 @@ class AllocationLoadManager { } this._active = undefined; this._paused = false; + this._eventsSinceLastTick = 0; + + // Public API + this.cycle = null; + this.testDurationMS = gDefaultTestDuration * 1000; + + // Constants + this.CYCLE_STARTED = 1; + this.CYCLE_STOPPED = 2; + this.LOAD_ENDED = 4; + this.LOAD_STARTED = 8; } activeLoad() { @@ -132,20 +185,58 @@ class AllocationLoadManager { } } - tick() { + tick(now = performance.now()) { + this.lastActive = this._active; + let events = this._eventsSinceLastTick; + this._eventsSinceLastTick = 0; + + if (this.cycle) { + const prev = this.cycle.current; + this.cycle.tick(now); + if (this.cycle.current != prev) { + if (this.cycle.current) { + this.setActiveLoadByName(this.cycle.current); + } else { + this.deactivateLoad(); + } + events |= this.LOAD_ENDED; + if (this.cycle.done()) { + events |= this.CYCLE_STOPPED; + this.cycle = null; + } else { + events |= this.LOAD_STARTED; + } + } + } + if (this._active && !this._paused) { this._active.tick(); } + + return events; + } + + startCycle(tests_to_run, now = performance.now()) { + this.cycle = new LoadCycle(tests_to_run, this.testDurationMS); + this.cycle.start(now); + this.setActiveLoadByName(this.cycle.current); + this._eventsSinceLastTick |= this.CYCLE_STARTED | this.LOAD_STARTED; + } + + cycleStopped() { + return !this.cycle || this.cycle.done(); + } + + cycleCurrentLoadRemaining(now = performance.now()) { + return this.cycleStopped() + ? 0 + : this.testDurationMS - this.cycle.currentLoadElapsed(now); } } // Current test state. var gLoadMgr = undefined; -var testDuration = undefined; // ms -var testStart = undefined; // ms -var testQueue = []; - function format_gcBytes(bytes) { if (bytes < 4000) { return `${bytes} bytes`; @@ -171,7 +262,7 @@ function compute_test_score(histogram) { for (let [delay, count] of histogram) { score += Math.abs((delay - 1000 / 60) * count); } - score = score / (testDuration / 1000); + score = score / (gLoadMgr.testDurationMS / 1000); return Math.round(score * 1000) / 1000; } diff --git a/js/src/devtools/gc-ubench/ui.js b/js/src/devtools/gc-ubench/ui.js index 234845eaefdf..7fd3696c0f57 100644 --- a/js/src/devtools/gc-ubench/ui.js +++ b/js/src/devtools/gc-ubench/ui.js @@ -431,17 +431,20 @@ function handler(timestamp) { return; } - if (testState === "running" && timestamp - testStart > testDuration) { - end_test(timestamp); + const events = gLoadMgr.tick(timestamp); + if (events & gLoadMgr.LOAD_ENDED) { + end_test(timestamp, gLoadMgr.lastActive); + if (!gLoadMgr.cycleStopped()) { + start_test(); + } } if (testState == "running") { document.getElementById("test-progress").textContent = - ((testDuration - (timestamp - testStart)) / 1000).toFixed(1) + " sec"; + (gLoadMgr.cycleCurrentLoadRemaining(timestamp) / 1000).toFixed(1) + + " sec"; } - gLoadMgr.tick(); - const delay = gFrameTimer.on_frame_finished(timestamp); update_histogram(gHistogram, delay); @@ -592,15 +595,9 @@ function run_all_tests() { 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); - } + gLoadMgr.startCycle(tests_to_run); testState = "running"; - testStart = performance.now(); gHistogram.clear(); - - start_test(testQueue.shift()); reset_draw_state(); } @@ -615,24 +612,19 @@ function update_load_state_indicator() { document.getElementById("load-running").textContent = loadState; } -function start_test(testName) { - change_load(testName); - console.log(`Running test: ${testName}`); - document.getElementById("test-selection").value = testName; +function start_test() { + console.log(`Running test: ${gLoadMgr.activeLoad().name}`); + document.getElementById("test-selection").value = gLoadMgr.activeLoad().name; update_load_state_indicator(); } -function end_test(timestamp) { +function end_test(timestamp, load) { document.getElementById("test-progress").textContent = "(not running)"; - report_test_result(gLoadMgr.activeLoad(), gHistogram); + report_test_result(load, gHistogram); gHistogram.clear(); - console.log(`Ending test ${gLoadMgr.activeLoad().name}`); - if (testQueue.length) { - start_test(testQueue.shift()); - testStart = timestamp; - } else { + console.log(`Ending test ${load.name}`); + if (gLoadMgr.cycleStopped()) { testState = "idle"; - testStart = 0; } reset_draw_state(); } @@ -699,8 +691,10 @@ function change_load(new_load_name) { function duration_changed() { var durationInput = document.getElementById("test-duration"); - testDuration = parseInt(durationInput.value) * 1000; - console.log(`Updated test duration to: ${testDuration / 1000} seconds`); + gLoadMgr.testDurationMS = parseInt(durationInput.value) * 1000; + console.log( + `Updated test duration to: ${gLoadMgr.testDurationMS / 1000} seconds` + ); } function onLoadChange() {