Commit 8057caf7 authored by Camillo Bruni's avatar Camillo Bruni Committed by V8 LUCI CQ

[tools][system-analyzer] Speed improvements

- Avoid redrawing property-link tables if the contents don't change
- Don't update timeline legends if the selection doesn't change
- Use shorter class names for the flamechart for faster parsing
- Round positions in flamechart to avoid long strings that would be
  created from raw double positions
- Don't redraw the tooltip if the content is the same

Change-Id: I925f1708400286c7c9f8db62f75c3b5fe8a16b12
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3521945Reviewed-by: 's avatarPatrick Thier <pthier@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79540}
parent 0b28b6e6
...@@ -34,6 +34,7 @@ export class V8CustomElement extends HTMLElement { ...@@ -34,6 +34,7 @@ export class V8CustomElement extends HTMLElement {
} }
forceUpdate() { forceUpdate() {
this._updateTimeoutId = undefined;
this._update(); this._update();
} }
......
...@@ -73,4 +73,25 @@ export function arrayEquals(left, right) { ...@@ -73,4 +73,25 @@ export function arrayEquals(left, right) {
return true; return true;
} }
export function entriesEquals(left, right) {
if (left == right) return true;
if (left == undefined) return right == undefined;
const leftEntries = Object.entries(left);
const rightEntries = Object.entries(right);
if (leftEntries.length !== rightEntries.length) return false;
for (let i = 0; i < leftEntries.length; i++) {
const l = leftEntries[i];
const r = rightEntries[i];
if (l[0] != r[0]) return false;
if (l[1] != r[1]) return false;
}
return true;
}
export function keysEquals(left, right) {
if (left == right) return true;
if (left == undefined) return right == undefined;
return arrayEquals(Object.keys(left), Object.keys(right));
}
export * from '../js/helper.mjs' export * from '../js/helper.mjs'
...@@ -47,7 +47,7 @@ found in the LICENSE file. --> ...@@ -47,7 +47,7 @@ found in the LICENSE file. -->
</head> </head>
<body> <body>
<tool-tip id="tool-tip"></tool-tip> <tool-tip id="tool-tip" style="will-change: transform"></tool-tip>
<section id="file-reader"> <section id="file-reader">
<log-file-reader id="log-file-reader"></log-file-reader> <log-file-reader id="log-file-reader"></log-file-reader>
......
...@@ -3,8 +3,9 @@ ...@@ -3,8 +3,9 @@
// found in the LICENSE file. // found in the LICENSE file.
import {App} from '../index.mjs' import {App} from '../index.mjs'
import {FocusEvent, SelectRelatedEvent} from './events.mjs'; import {FocusEvent, SelectRelatedEvent} from './events.mjs';
import {DOM, ExpandableText, V8CustomElement} from './helper.mjs'; import {DOM, entriesEquals, ExpandableText, V8CustomElement} from './helper.mjs';
DOM.defineCustomElement('view/property-link-table', DOM.defineCustomElement('view/property-link-table',
template => template =>
...@@ -27,7 +28,7 @@ DOM.defineCustomElement('view/property-link-table', ...@@ -27,7 +28,7 @@ DOM.defineCustomElement('view/property-link-table',
} }
set propertyDict(propertyDict) { set propertyDict(propertyDict) {
if (this._propertyDict === propertyDict) return; if (entriesEquals(this._propertyDict, propertyDict)) return;
if (typeof propertyDict !== 'object') { if (typeof propertyDict !== 'object') {
throw new Error( throw new Error(
`Invalid property dict, expected object: ${propertyDict}`); `Invalid property dict, expected object: ${propertyDict}`);
......
...@@ -510,6 +510,7 @@ class SelectionHandler { ...@@ -510,6 +510,7 @@ class SelectionHandler {
class Legend { class Legend {
_timeline; _timeline;
_lastSelection;
_typesFilters = new Map(); _typesFilters = new Map();
_typeClickHandler = this._handleTypeClick.bind(this); _typeClickHandler = this._handleTypeClick.bind(this);
_filterPredicate = this.filter.bind(this); _filterPredicate = this.filter.bind(this);
...@@ -553,6 +554,8 @@ class Legend { ...@@ -553,6 +554,8 @@ class Legend {
} }
update() { update() {
if (this._lastSelection === this.selection) return;
this._lastSelection = this.selection;
const tbody = DOM.tbody(); const tbody = DOM.tbody();
const missingTypes = new Set(this._typesFilters.keys()); const missingTypes = new Set(this._typesFilters.keys());
this._checkDurationField(); this._checkDurationField();
......
...@@ -108,19 +108,19 @@ export class TimelineTrackStackedBase extends TimelineTrackBase { ...@@ -108,19 +108,19 @@ export class TimelineTrackStackedBase extends TimelineTrackBase {
} }
_drawItem(item, i, outline = false) { _drawItem(item, i, outline = false) {
const x = this.timeToPosition(item.time); const x = roundTo3Digits(this.timeToPosition(item.time));
const y = (item.depth + 1) * kItemHeight; const y = (item.depth + 1) * kItemHeight;
let width = item.duration * this._timeToPixel; let width = roundTo3Digits(item.duration * this._timeToPixel);
if (outline) { if (outline) {
return `<rect x=${x} y=${y} width=${width} height=${ return `<rect x=${x} y=${y} width=${width} height=${
kItemHeight - 1} class=flameSelected />`; kItemHeight - 1} class=fs />`;
} }
let color = this._legend.colorForType(item.type); let color = this._legend.colorForType(item.type);
if (i % 2 == 1) { if (i % 2 == 1) {
color = CSSColor.darken(color, 20); color = CSSColor.darken(color, 20);
} }
return `<rect x=${x} y=${y} width=${width} height=${kItemHeight - 1} fill=${ return `<rect x=${x} y=${y} width=${width} height=${kItemHeight - 1} fill=${
color} class=flame />`; color} class=f />`;
} }
_drawItemText(item) { _drawItemText(item) {
...@@ -141,4 +141,8 @@ export class TimelineTrackStackedBase extends TimelineTrackBase { ...@@ -141,4 +141,8 @@ export class TimelineTrackStackedBase extends TimelineTrackBase {
buffer += `<text x=${x + 1} y=${y - 3} class=txt>${text}</text>` buffer += `<text x=${x + 1} y=${y - 3} class=txt>${text}</text>`
return buffer; return buffer;
} }
} }
\ No newline at end of file
function roundTo3Digits(value) {
return ((value * 1000) | 0) / 1000;
}
...@@ -184,10 +184,10 @@ found in the LICENSE file. --> ...@@ -184,10 +184,10 @@ found in the LICENSE file. -->
dominant-baseline: hanging; dominant-baseline: hanging;
font-size: 9px; font-size: 9px;
} }
.flame { .flame, .f {
stroke-width: 0; stroke-width: 0;
} }
.flameSelected { .flameSelected, .fs {
fill: var(--on-background-color); fill: var(--on-background-color);
fill-opacity: 0.1; fill-opacity: 0.1;
stroke: var(--on-background-color); stroke: var(--on-background-color);
...@@ -234,4 +234,4 @@ found in the LICENSE file. --> ...@@ -234,4 +234,4 @@ found in the LICENSE file. -->
<tbody></tbody> <tbody></tbody>
</table> </table>
</div> </div>
</div> </div>
\ No newline at end of file
...@@ -152,7 +152,7 @@ class Annotations { ...@@ -152,7 +152,7 @@ class Annotations {
const start = this._flames.find(time); const start = this._flames.find(time);
let offset = 0; let offset = 0;
// Draw annotations gradually outwards starting form the given time. // Draw annotations gradually outwards starting form the given time.
let deadline = performance.now() + 500; let deadline = performance.now() + 100;
for (let range = 0; range < this._flames.length; range += 10000) { for (let range = 0; range < this._flames.length; range += 10000) {
this._markFlames(start - range, start - offset); this._markFlames(start - range, start - offset);
this._markFlames(start + offset, start + range); this._markFlames(start + offset, start + range);
...@@ -165,7 +165,7 @@ class Annotations { ...@@ -165,7 +165,7 @@ class Annotations {
// Abort if we started another update asynchronously. // Abort if we started another update asynchronously.
if (this._logEntry != logEntry) return; if (this._logEntry != logEntry) return;
deadline = performance.now() + 500; deadline = performance.now() + 100;
} }
this._drawBuffer(); this._drawBuffer();
} }
......
...@@ -8,6 +8,15 @@ found in the LICENSE file. --> ...@@ -8,6 +8,15 @@ found in the LICENSE file. -->
:host { :host {
position: absolute; position: absolute;
z-index: 100; z-index: 100;
will-change: transform;
}
#body {
display: none;
position: absolute;
--tip-offset: 10px;
--tip-width: 10px;
--tip-height: 40px;
} }
#content { #content {
...@@ -36,15 +45,6 @@ found in the LICENSE file. --> ...@@ -36,15 +45,6 @@ found in the LICENSE file. -->
max-width: 500px; max-width: 500px;
} }
#body {
display: none;
position: absolute;
z-index: 99999;
--tip-offset: 10px;
--tip-width: 10px;
--tip-height: 40px;
}
#body.top { #body.top {
bottom: var(--tip-height); bottom: var(--tip-height);
} }
......
...@@ -87,6 +87,9 @@ DOM.defineCustomElement( ...@@ -87,6 +87,9 @@ DOM.defineCustomElement(
set content(content) { set content(content) {
if (!content) return this.hide(); if (!content) return this.hide();
this.show(); this.show();
if (this._content === content) return;
this._content = content;
if (typeof content === 'string') { if (typeof content === 'string') {
this.contentNode.innerHTML = content; this.contentNode.innerHTML = content;
this.contentNode.className = 'textContent'; this.contentNode.className = 'textContent';
...@@ -112,12 +115,15 @@ DOM.defineCustomElement( ...@@ -112,12 +115,15 @@ DOM.defineCustomElement(
} }
hide() { hide() {
this._content = undefined;
if (this._isHidden) return;
this._isHidden = true; this._isHidden = true;
this.bodyNode.style.display = 'none'; this.bodyNode.style.display = 'none';
this.targetNode = undefined; this.targetNode = undefined;
} }
show() { show() {
if (!this._isHidden) return;
this.bodyNode.style.display = 'block'; this.bodyNode.style.display = 'block';
this._isHidden = false; this._isHidden = false;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment