mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-06 15:58:39 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
342
devtools/client/performance/modules/logic/jit.js
Normal file
342
devtools/client/performance/modules/logic/jit.js
Normal file
|
|
@ -0,0 +1,342 @@
|
|||
/* 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/. */
|
||||
"use strict";
|
||||
|
||||
// An outcome of an OptimizationAttempt that is considered successful.
|
||||
const SUCCESSFUL_OUTCOMES = [
|
||||
"GenericSuccess", "Inlined", "DOM", "Monomorphic", "Polymorphic"
|
||||
];
|
||||
|
||||
/**
|
||||
* Model representing JIT optimization sites from the profiler
|
||||
* for a frame (represented by a FrameNode). Requires optimization data from
|
||||
* a profile, which is an array of RawOptimizationSites.
|
||||
*
|
||||
* When the ThreadNode for the profile iterates over the samples' frames, each
|
||||
* frame's optimizations are accumulated in their respective FrameNodes. Each
|
||||
* FrameNode may contain many different optimization sites. One sample may
|
||||
* pick up optimization X on line Y in the frame, with the next sample
|
||||
* containing optimization Z on line W in the same frame, as each frame is
|
||||
* only function.
|
||||
*
|
||||
* An OptimizationSite contains a record of how many times the
|
||||
* RawOptimizationSite was sampled, as well as the unique id based off of the
|
||||
* original profiler array, and the RawOptimizationSite itself as a reference.
|
||||
* @see devtools/client/performance/modules/logic/tree-model.js
|
||||
*
|
||||
* @struct RawOptimizationSite
|
||||
* A structure describing a location in a script that was attempted to be optimized.
|
||||
* Contains all the IonTypes observed, and the sequence of OptimizationAttempts that
|
||||
* were attempted, and the line and column in the script. This is retrieved from the
|
||||
* profiler after a recording, and our base data structure. Should always be referenced,
|
||||
* and unmodified.
|
||||
*
|
||||
* Note that propertyName is an index into a string table, which needs to be
|
||||
* provided in order for the raw optimization site to be inflated.
|
||||
*
|
||||
* @type {Array<IonType>} types
|
||||
* @type {Array<OptimizationAttempt>} attempts
|
||||
* @type {?number} propertyName
|
||||
* @type {number} line
|
||||
* @type {number} column
|
||||
*
|
||||
*
|
||||
* @struct IonType
|
||||
* IonMonkey attempts to classify each value in an optimization site by some type.
|
||||
* Based off of the observed types for a value (like a variable that could be a
|
||||
* string or an instance of an object), it determines what kind of type it should be
|
||||
* classified as. Each IonType here contains an array of all ObservedTypes under `types`,
|
||||
* the Ion type that IonMonkey decided this value should be (Int32, Object, etc.) as
|
||||
* `mirType`, and the component of this optimization type that this value refers to --
|
||||
* like a "getter" optimization, `a[b]`, has site `a` (the "Receiver") and `b`
|
||||
* (the "Index").
|
||||
*
|
||||
* Generally the more ObservedTypes, the more deoptimized this OptimizationSite is.
|
||||
* There could be no ObservedTypes, in which case `typeset` is undefined.
|
||||
*
|
||||
* @type {?Array<ObservedType>} typeset
|
||||
* @type {string} site
|
||||
* @type {string} mirType
|
||||
*
|
||||
*
|
||||
* @struct ObservedType
|
||||
* When IonMonkey attempts to determine what type a value is, it checks on each sample.
|
||||
* The ObservedType can be thought of in more of JavaScripty-terms, rather than C++.
|
||||
* The `keyedBy` property is a high level description of the type, like "primitive",
|
||||
* "constructor", "function", "singleton", "alloc-site" (that one is a bit more weird).
|
||||
* If the `keyedBy` type is a function or constructor, the ObservedType should have a
|
||||
* `name` property, referring to the function or constructor name from the JS source.
|
||||
* If IonMonkey can determine the origin of this type (like where the constructor is
|
||||
* defined), the ObservedType will also have `location` and `line` properties, but
|
||||
* `location` can sometimes be non-URL strings like "self-hosted" or a memory location
|
||||
* like "102ca7880", or no location at all, and maybe `line` is 0 or undefined.
|
||||
*
|
||||
* @type {string} keyedBy
|
||||
* @type {?string} name
|
||||
* @type {?string} location
|
||||
* @type {?string} line
|
||||
*
|
||||
*
|
||||
* @struct OptimizationAttempt
|
||||
* Each RawOptimizationSite contains an array of OptimizationAttempts. Generally,
|
||||
* IonMonkey goes through a series of strategies for each kind of optimization, starting
|
||||
* from most-niche and optimized, to the less-optimized, but more general strategies --
|
||||
* for example, a getter opt may first try to optimize for the scenario of a getter on an
|
||||
* `arguments` object -- that will fail most of the time, as most objects are not
|
||||
* arguments objects, but it will attempt several strategies in order until it finds a
|
||||
* strategy that works, or fails. Even in the best scenarios, some attempts will fail
|
||||
* (like the arguments getter example), which is OK, as long as some attempt succeeds
|
||||
* (with the earlier attempts preferred, as those are more optimized). In an
|
||||
* OptimizationAttempt structure, we store just the `strategy` name and `outcome` name,
|
||||
* both from enums in js/public/TrackedOptimizationInfo.h as TRACKED_STRATEGY_LIST and
|
||||
* TRACKED_OUTCOME_LIST, respectively. An array of successful outcome strings are above
|
||||
* in SUCCESSFUL_OUTCOMES.
|
||||
*
|
||||
* @see js/public/TrackedOptimizationInfo.h
|
||||
*
|
||||
* @type {string} strategy
|
||||
* @type {string} outcome
|
||||
*/
|
||||
|
||||
/*
|
||||
* A wrapper around RawOptimizationSite to record sample count and ID (referring to the
|
||||
* index of where this is in the initially seeded optimizations data), so we don't mutate
|
||||
* the original data from the profiler. Provides methods to access the underlying
|
||||
* optimization data easily, so understanding the semantics of JIT data isn't necessary.
|
||||
*
|
||||
* @constructor
|
||||
*
|
||||
* @param {Array<RawOptimizationSite>} optimizations
|
||||
* @param {number} optsIndex
|
||||
*
|
||||
* @type {RawOptimizationSite} data
|
||||
* @type {number} samples
|
||||
* @type {number} id
|
||||
*/
|
||||
|
||||
const OptimizationSite = function (id, opts) {
|
||||
this.id = id;
|
||||
this.data = opts;
|
||||
this.samples = 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor for JITOptimizations. A collection of OptimizationSites for a frame.
|
||||
*
|
||||
* @constructor
|
||||
* @param {Array<RawOptimizationSite>} rawSites
|
||||
* Array of raw optimization sites.
|
||||
* @param {Array<string>} stringTable
|
||||
* Array of strings from the profiler used to inflate
|
||||
* JIT optimizations. Do not modify this!
|
||||
*/
|
||||
|
||||
const JITOptimizations = function (rawSites, stringTable) {
|
||||
// Build a histogram of optimization sites.
|
||||
let sites = [];
|
||||
|
||||
for (let rawSite of rawSites) {
|
||||
let existingSite = sites.find((site) => site.data === rawSite);
|
||||
if (existingSite) {
|
||||
existingSite.samples++;
|
||||
} else {
|
||||
sites.push(new OptimizationSite(sites.length, rawSite));
|
||||
}
|
||||
}
|
||||
|
||||
// Inflate the optimization information.
|
||||
for (let site of sites) {
|
||||
let data = site.data;
|
||||
let STRATEGY_SLOT = data.attempts.schema.strategy;
|
||||
let OUTCOME_SLOT = data.attempts.schema.outcome;
|
||||
let attempts = data.attempts.data.map((a) => {
|
||||
return {
|
||||
id: site.id,
|
||||
strategy: stringTable[a[STRATEGY_SLOT]],
|
||||
outcome: stringTable[a[OUTCOME_SLOT]]
|
||||
};
|
||||
});
|
||||
let types = data.types.map((t) => {
|
||||
let typeset = maybeTypeset(t.typeset, stringTable);
|
||||
if (typeset) {
|
||||
typeset.forEach(ts => {
|
||||
ts.id = site.id;
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
id: site.id,
|
||||
typeset,
|
||||
site: stringTable[t.site],
|
||||
mirType: stringTable[t.mirType]
|
||||
};
|
||||
});
|
||||
// Add IDs to to all children objects, so we can correllate sites when
|
||||
// just looking at a specific type, attempt, etc..
|
||||
attempts.id = types.id = site.id;
|
||||
|
||||
site.data = {
|
||||
attempts,
|
||||
types,
|
||||
propertyName: maybeString(stringTable, data.propertyName),
|
||||
line: data.line,
|
||||
column: data.column
|
||||
};
|
||||
}
|
||||
|
||||
this.optimizationSites = sites.sort((a, b) => b.samples - a.samples);
|
||||
};
|
||||
|
||||
/**
|
||||
* Make JITOptimizations iterable.
|
||||
*/
|
||||
JITOptimizations.prototype = {
|
||||
[Symbol.iterator]: function* () {
|
||||
yield* this.optimizationSites;
|
||||
},
|
||||
|
||||
get length() {
|
||||
return this.optimizationSites.length;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes an "outcome" string from an OptimizationAttempt and returns
|
||||
* a boolean indicating whether or not its a successful outcome.
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
|
||||
function isSuccessfulOutcome(outcome) {
|
||||
return !!~SUCCESSFUL_OUTCOMES.indexOf(outcome);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an OptimizationSite. Returns a boolean indicating if the passed
|
||||
* in OptimizationSite has a "good" outcome at the end of its attempted strategies.
|
||||
*
|
||||
* @param {OptimizationSite} optimizationSite
|
||||
* @return {boolean}
|
||||
*/
|
||||
|
||||
function hasSuccessfulOutcome(optimizationSite) {
|
||||
let attempts = optimizationSite.data.attempts;
|
||||
let lastOutcome = attempts[attempts.length - 1].outcome;
|
||||
return isSuccessfulOutcome(lastOutcome);
|
||||
}
|
||||
|
||||
function maybeString(stringTable, index) {
|
||||
return index ? stringTable[index] : undefined;
|
||||
}
|
||||
|
||||
function maybeTypeset(typeset, stringTable) {
|
||||
if (!typeset) {
|
||||
return undefined;
|
||||
}
|
||||
return typeset.map((ty) => {
|
||||
return {
|
||||
keyedBy: maybeString(stringTable, ty.keyedBy),
|
||||
name: maybeString(stringTable, ty.name),
|
||||
location: maybeString(stringTable, ty.location),
|
||||
line: ty.line
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// Map of optimization implementation names to an enum.
|
||||
const IMPLEMENTATION_MAP = {
|
||||
"interpreter": 0,
|
||||
"baseline": 1,
|
||||
"ion": 2
|
||||
};
|
||||
const IMPLEMENTATION_NAMES = Object.keys(IMPLEMENTATION_MAP);
|
||||
|
||||
/**
|
||||
* Takes data from a FrameNode and computes rendering positions for
|
||||
* a stacked mountain graph, to visualize JIT optimization tiers over time.
|
||||
*
|
||||
* @param {FrameNode} frameNode
|
||||
* The FrameNode who's optimizations we're iterating.
|
||||
* @param {Array<number>} sampleTimes
|
||||
* An array of every sample time within the range we're counting.
|
||||
* From a ThreadNode's `sampleTimes` property.
|
||||
* @param {number} bucketSize
|
||||
* Size of each bucket in milliseconds.
|
||||
* `duration / resolution = bucketSize` in OptimizationsGraph.
|
||||
* @return {?Array<object>}
|
||||
*/
|
||||
function createTierGraphDataFromFrameNode(frameNode, sampleTimes, bucketSize) {
|
||||
let tierData = frameNode.getTierData();
|
||||
let stringTable = frameNode._stringTable;
|
||||
let output = [];
|
||||
let implEnum;
|
||||
|
||||
let tierDataIndex = 0;
|
||||
let nextOptSample = tierData[tierDataIndex];
|
||||
|
||||
// Bucket data
|
||||
let samplesInCurrentBucket = 0;
|
||||
let currentBucketStartTime = sampleTimes[0];
|
||||
let bucket = [];
|
||||
|
||||
// Store previous data point so we can have straight vertical lines
|
||||
let previousValues;
|
||||
|
||||
// Iterate one after the samples, so we can finalize the last bucket
|
||||
for (let i = 0; i <= sampleTimes.length; i++) {
|
||||
let sampleTime = sampleTimes[i];
|
||||
|
||||
// If this sample is in the next bucket, or we're done
|
||||
// checking sampleTimes and on the last iteration, finalize previous bucket
|
||||
if (sampleTime >= (currentBucketStartTime + bucketSize) ||
|
||||
i >= sampleTimes.length) {
|
||||
let dataPoint = {};
|
||||
dataPoint.values = [];
|
||||
dataPoint.delta = currentBucketStartTime;
|
||||
|
||||
// Map the opt site counts as a normalized percentage (0-1)
|
||||
// of its count in context of total samples this bucket
|
||||
for (let j = 0; j < IMPLEMENTATION_NAMES.length; j++) {
|
||||
dataPoint.values[j] = (bucket[j] || 0) / (samplesInCurrentBucket || 1);
|
||||
}
|
||||
|
||||
// Push the values from the previous bucket to the same time
|
||||
// as the current bucket so we get a straight vertical line.
|
||||
if (previousValues) {
|
||||
let data = Object.create(null);
|
||||
data.values = previousValues;
|
||||
data.delta = currentBucketStartTime;
|
||||
output.push(data);
|
||||
}
|
||||
|
||||
output.push(dataPoint);
|
||||
|
||||
// Set the new start time of this bucket and reset its count
|
||||
currentBucketStartTime += bucketSize;
|
||||
samplesInCurrentBucket = 0;
|
||||
previousValues = dataPoint.values;
|
||||
bucket = [];
|
||||
}
|
||||
|
||||
// If this sample observed an optimization in this frame, record it
|
||||
if (nextOptSample && nextOptSample.time === sampleTime) {
|
||||
// If no implementation defined, it was the "interpreter".
|
||||
implEnum = IMPLEMENTATION_MAP[stringTable[nextOptSample.implementation] ||
|
||||
"interpreter"];
|
||||
bucket[implEnum] = (bucket[implEnum] || 0) + 1;
|
||||
nextOptSample = tierData[++tierDataIndex];
|
||||
}
|
||||
|
||||
samplesInCurrentBucket++;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
exports.createTierGraphDataFromFrameNode = createTierGraphDataFromFrameNode;
|
||||
exports.OptimizationSite = OptimizationSite;
|
||||
exports.JITOptimizations = JITOptimizations;
|
||||
exports.hasSuccessfulOutcome = hasSuccessfulOutcome;
|
||||
exports.isSuccessfulOutcome = isSuccessfulOutcome;
|
||||
exports.SUCCESSFUL_OUTCOMES = SUCCESSFUL_OUTCOMES;
|
||||
Loading…
Add table
Add a link
Reference in a new issue