The Performance Manager supports data-driven, centralized resource management, prioritization and planning for the Chrome browser. Over time, data-driven, centralized resource management will supplant and/or supplement the heuristics that can be found all over Chrome at present.
Here’s an overview picture of the intended architecture:
The graph lives on the Performance Manager Sequence, as do all graph observers and mutators. The graph structure can be viewed in the graph tab of the chrome://discards WebUI.
In addition to the above, the Performance Manager also provides support for users that need to occasionally query the graph to e.g. collect metrics.
The performance manager exposes a simplified, coarse model of the browser’s state as a Graph of Nodes, where the nodes represent such things as:
Nodes in the graph are connected with edges, such that e.g. each frame or worker connects to its hosting process. Frames are connected in a tree, and all frames in a tree connect to their corresponding page. Other edge types may denote dependencies between nodes, such as e.g. a MessagePort connection or the like. Different node types in the graph are adorned with different sets of properties, each of which represents necessary information about the particular node type. The graph provides node lookup and retrieval, as well as an observer interface for notification of addition and removal of nodes, as well as node property changes. The graph also provides a way for users to adorn nodes with private data by means of Node Attached Data. It’s preferable to store data that’s used for intermediate results or for private purposes in Node Attached Data rather than in Node properties.
The graph exposes both a public and a private API. The private API is only available to code in the component’s directory, whereas the public API can be used by anyone in the browser process. The private API provides read-write access to nodes and allows for node-intimate storage of node attached properties. The public API provides read-only access to nodes, but otherwise the two APIs are fairly similar. Where possible, using the public API is preferable, as the public API is more stable, and doesn’t require the user to live in the component.
Good examples where the public API is appropriate, is e.g. for periodically collecting metrics by iteration over the graph, or where iterating over the graph can yield a set of content entities for some operation. As an example, finding the set of frames in a page that belong to the same browsing instance would be easy to do by iterating over the graph.
Decorators set properties on the graph that are derived from data external to the graph itself, such as e.g. performance measurement data.
A simple example Decorator might periodically measure the cumulative CPU usage for each process Node in the Graph and adorn each node with the measured cumulative CPU usage of the corresponding process.
Decorators can make data available to consumers by updating core properties of a node or by exposing their own custom API. A common interface is to provide an associated Data class, with a static accessor to retrieve a Data object from a given Node. See PageLiveStateDecorator::Data for an example.
Each node type provides an observer interface that callers can implement to receive notifications when core node properties change. Data that is exposed through a decorator or Node Attached Data may or may not provide change notifications depending on the details of the decorator's API.
Aggregators, like Decorators, set properties on the graph nodes. The difference is that Aggregators work solely on data in the graph, to e.g. aggregate or distribute data from one node to another.
A simple example Aggregator working with the simple example Decorator above might compute the difference in cumulative CPU usage from the most recent measurement, then distribute the difference across the Frame (and/or Worker) nodes associated with each Process. This would in turn allow summing up the cumulative CPU usage of each Frame and Worker node to their associated Page node. This would yield the CPU usage of the entire Page (content::WebContents).
The intent is for all Resource Management Policies to use the graph and the data therein to make policy decisions. The decisions are then implemented by invoking some kind of Resource Management Mechanism. Making this a clean distinction makes the implementation easier, more readable and more testable.
A simple example of a policy and a mechanism might be a policy for flushing some kind of a cache after all frames in a process have been backgrounded or idle for a set amount of time. This policy would register for the appropriate notifications and likely maintain private state on a process node. When the criteria for flushing is met, the policy invokes the mechanism by rendezvousing to a process-associated mojo interface and invoking on it.
If the mojo interface is accessible through a content entity (e.g. content::WebContents), the mechanism invocation is posted to the main thread where the relevant content proxy (e.g. performance_manager::WebContentsProxy) makes it easy and safe to retrieve the content entity on the main thread. Should the corresponding content entity have been deleted after the task was posted, the content proxy will simply return nullptr.