summaryrefslogtreecommitdiffstats
path: root/js/src/devtools/gc-ubench/perf.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/devtools/gc-ubench/perf.js')
-rw-r--r--js/src/devtools/gc-ubench/perf.js217
1 files changed, 217 insertions, 0 deletions
diff --git a/js/src/devtools/gc-ubench/perf.js b/js/src/devtools/gc-ubench/perf.js
new file mode 100644
index 0000000000..dc370ee0da
--- /dev/null
+++ b/js/src/devtools/gc-ubench/perf.js
@@ -0,0 +1,217 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Performance monitoring and calculation.
+
+function round_up(val, interval) {
+ return val + (interval - (val % interval));
+}
+
+// Class for inter-frame timing, which handles being paused and resumed.
+var FrameTimer = class {
+ constructor() {
+ // Start time of the current active test, adjusted for any time spent
+ // stopped (so `now - this.start` is how long the current active test
+ // has run for.)
+ this.start = undefined;
+
+ // Timestamp of callback following the previous frame.
+ this.prev = undefined;
+
+ // Timestamp when drawing was paused, or zero if drawing is active.
+ this.stopped = 0;
+ }
+
+ is_stopped() {
+ return this.stopped != 0;
+ }
+
+ start_recording(now = gHost.now()) {
+ this.start = this.prev = now;
+ }
+
+ on_frame_finished(now = gHost.now()) {
+ const delay = now - this.prev;
+ this.prev = now;
+ return delay;
+ }
+
+ pause(now = gHost.now()) {
+ this.stopped = now;
+ // Abuse this.prev to store the time elapsed since the previous frame.
+ // This will be used to adjust this.prev when we resume.
+ this.prev = now - this.prev;
+ }
+
+ resume(now = gHost.now()) {
+ this.prev = now - this.prev;
+ const stop_duration = now - this.stopped;
+ this.start += stop_duration;
+ this.stopped = 0;
+ }
+};
+
+// Per-frame time sampling infra.
+var sampleTime = 16.666667; // ms
+var sampleIndex = 0;
+
+// Class for maintaining a rolling window of per-frame GC-related counters:
+// inter-frame delay, minor/major/slice GC counts, cumulative bytes, etc.
+var FrameHistory = class {
+ constructor(numSamples) {
+ // Private
+ this._frameTimer = new FrameTimer();
+ this._numSamples = numSamples;
+
+ // Public API
+ this.delays = new Array(numSamples);
+ this.gcBytes = new Array(numSamples);
+ this.mallocBytes = new Array(numSamples);
+ this.gcs = new Array(numSamples);
+ this.minorGCs = new Array(numSamples);
+ this.majorGCs = new Array(numSamples);
+ this.slices = new Array(numSamples);
+
+ sampleIndex = 0;
+ this.reset();
+ }
+
+ start(now = gHost.now()) {
+ this._frameTimer.start_recording(now);
+ }
+
+ reset() {
+ this.delays.fill(0);
+ this.gcBytes.fill(0);
+ this.mallocBytes.fill(0);
+ this.gcs.fill(this.gcs[sampleIndex]);
+ this.minorGCs.fill(this.minorGCs[sampleIndex]);
+ this.majorGCs.fill(this.majorGCs[sampleIndex]);
+ this.slices.fill(this.slices[sampleIndex]);
+
+ sampleIndex = 0;
+ }
+
+ get numSamples() {
+ return this._numSamples;
+ }
+
+ findMax(collection) {
+ // Depends on having at least one non-negative entry, and unfilled
+ // entries being <= max.
+ var maxIndex = 0;
+ for (let i = 0; i < this._numSamples; i++) {
+ if (collection[i] >= collection[maxIndex]) {
+ maxIndex = i;
+ }
+ }
+ return maxIndex;
+ }
+
+ findMaxDelay() {
+ return this.findMax(this.delays);
+ }
+
+ on_frame(now = gHost.now()) {
+ const delay = this._frameTimer.on_frame_finished(now);
+
+ // Total time elapsed while the active test has been running.
+ var t = now - this._frameTimer.start;
+ var newIndex = Math.round(t / sampleTime);
+ while (sampleIndex < newIndex) {
+ sampleIndex++;
+ var idx = sampleIndex % this._numSamples;
+ this.delays[idx] = delay;
+ if (gHost.features.haveMemorySizes) {
+ this.gcBytes[idx] = gHost.gcBytes;
+ this.mallocBytes[idx] = gHost.mallocBytes;
+ }
+ if (gHost.features.haveGCCounts) {
+ this.minorGCs[idx] = gHost.minorGCCount;
+ this.majorGCs[idx] = gHost.majorGCCount;
+ this.slices[idx] = gHost.GCSliceCount;
+ }
+ }
+
+ return delay;
+ }
+
+ pause() {
+ this._frameTimer.pause();
+ }
+
+ resume() {
+ this._frameTimer.resume();
+ }
+
+ is_stopped() {
+ return this._frameTimer.is_stopped();
+ }
+};
+
+var PerfTracker = class {
+ constructor() {
+ // Private
+ this._currentLoadStart = undefined;
+ this._frameCount = undefined;
+ this._mutating_ms = undefined;
+ this._suspend_sec = undefined;
+ this._minorGCs = undefined;
+ this._majorGCs = undefined;
+
+ // Public
+ this.results = [];
+ }
+
+ on_load_start(load, now = gHost.now()) {
+ this._currentLoadStart = now;
+ this._frameCount = 0;
+ this._mutating_ms = 0;
+ this._suspend_sec = 0;
+ this._majorGCs = gHost.majorGCCount;
+ this._minorGCs = gHost.minorGCCount;
+ }
+
+ on_load_end(load, now = gHost.now()) {
+ const elapsed_time = (now - this._currentLoadStart) / 1000;
+ const full_time = round_up(elapsed_time, 1 / 60);
+ const frame_60fps_limit = Math.round(full_time * 60);
+ const dropped_60fps_frames = frame_60fps_limit - this._frameCount;
+ const dropped_60fps_fraction = dropped_60fps_frames / frame_60fps_limit;
+
+ const mutating_and_gc_fraction = this._mutating_ms / (full_time * 1000);
+
+ const result = {
+ load,
+ elapsed_time,
+ mutating: this._mutating_ms / 1000,
+ mutating_and_gc_fraction,
+ suspended: this._suspend_sec,
+ full_time,
+ frames: this._frameCount,
+ dropped_60fps_frames,
+ dropped_60fps_fraction,
+ majorGCs: gHost.majorGCCount - this._majorGCs,
+ minorGCs: gHost.minorGCCount - this._minorGCs,
+ };
+ this.results.push(result);
+
+ this._currentLoadStart = undefined;
+ this._frameCount = 0;
+
+ return result;
+ }
+
+ after_suspend(wait_sec) {
+ this._suspend_sec += wait_sec;
+ }
+
+ before_mutator(now = gHost.now()) {
+ this._frameCount++;
+ }
+
+ after_mutator(start_time, end_time = gHost.now()) {
+ this._mutating_ms += end_time - start_time;
+ }
+};