Skip to content

Commit c546705

Browse files
Guangyue XuDevtools-frontend LUCI CQ
authored andcommitted
Update slow path non matches and add a test case
slowPathNonMatches = (1.0 - (fastReject / (matchAttempt - matchCount))) x 100% When the value is zero, all selectors are fast rejected in the match attempt. When value is 100, no selector is fast rejected in the match attempt. The lower the better. Bug: 379441923 Change-Id: Ie648a74174a85cab5edeac018c6052dbdf49da4c Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6700058 Reviewed-by: Paul Irish <[email protected]> Commit-Queue: Guangyue Xu <[email protected]>
1 parent 0e0de1d commit c546705

File tree

5 files changed

+92
-11
lines changed

5 files changed

+92
-11
lines changed

front_end/panels/timeline/TimelineSelectorStatsView.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ const UIStrings = {
3636
/**
3737
*@description Column name and percentage of slow mach non-matches computing a style rule
3838
*/
39-
rejectPercentage: '% of slow-path non-matches',
39+
slowPathNonMatches: '% of slow-path non-matches',
4040
/**
4141
*@description Tooltip description '% of slow-path non-matches'
4242
*/
43-
rejectPercentageExplanation:
43+
slowPathNonMatchesExplanation:
4444
'The percentage of non-matching nodes (Match Attempts - Match Count) that couldn\'t be quickly ruled out by the bloom filter due to high selector complexity. Lower is better.',
4545
/**
4646
*@description Column name for count of elements that the engine attempted to match against a style rule
@@ -165,8 +165,8 @@ export class TimelineSelectorStatsView extends UI.Widget.VBox {
165165
${i18nString(UIStrings.matchCount)}</span>
166166
</th>
167167
<th id=${SelectorTimingsKey.RejectPercentage} weight="1" sortable hideable align="right">
168-
<span title=${i18nString(UIStrings.rejectPercentageExplanation)}>${
169-
i18nString(UIStrings.rejectPercentage)}</span>
168+
<span title=${i18nString(UIStrings.slowPathNonMatchesExplanation)}>${
169+
i18nString(UIStrings.slowPathNonMatches)}</span>
170170
</th>
171171
<th id=${SelectorTimingsKey.Selector} weight="3" sortable hideable>
172172
<span title=${i18nString(UIStrings.selectorExplanation)}>
@@ -179,7 +179,8 @@ export class TimelineSelectorStatsView extends UI.Widget.VBox {
179179
</tr>
180180
${input.timings.map(timing => {
181181
const nonMatches = timing[SelectorTimingsKey.MatchAttempts] - timing[SelectorTimingsKey.MatchCount];
182-
const rejectPercentage = (nonMatches ? timing[SelectorTimingsKey.FastRejectCount] / nonMatches : 1) * 100;
182+
const slowPathNonMatches =
183+
(nonMatches ? 1.0 - timing[SelectorTimingsKey.FastRejectCount] / nonMatches : 0) * 100;
183184
const styleSheetId = timing[SelectorTimingsKey.StyleSheetId];
184185
const locations = timing.locations;
185186
const locationMessage = locations ? null :
@@ -194,8 +195,8 @@ export class TimelineSelectorStatsView extends UI.Widget.VBox {
194195
</td>
195196
<td>${timing[SelectorTimingsKey.MatchAttempts]}</td>
196197
<td>${timing[SelectorTimingsKey.MatchCount]}</td>
197-
<td data-value=${rejectPercentage}>
198-
${rejectPercentage.toFixed(1)}
198+
<td data-value=${slowPathNonMatches}>
199+
${slowPathNonMatches.toFixed(1)}
199200
</td>
200201
<td title=${timing[SelectorTimingsKey.Selector]}>
201202
${timing[SelectorTimingsKey.Selector]}
@@ -229,12 +230,13 @@ export class TimelineSelectorStatsView extends UI.Widget.VBox {
229230
const tableData = [];
230231
const columnName = [
231232
i18nString(UIStrings.elapsed), i18nString(UIStrings.matchAttempts), i18nString(UIStrings.matchCount),
232-
i18nString(UIStrings.rejectPercentage), i18nString(UIStrings.selector), i18nString(UIStrings.styleSheetId)
233+
i18nString(UIStrings.slowPathNonMatches), i18nString(UIStrings.selector), i18nString(UIStrings.styleSheetId)
233234
];
234235
tableData.push(columnName.join('\t'));
235236
for (const timing of this.#timings) {
236237
const nonMatches = timing[SelectorTimingsKey.MatchAttempts] - timing[SelectorTimingsKey.MatchCount];
237-
const rejectPercentage = (nonMatches ? timing[SelectorTimingsKey.FastRejectCount] / nonMatches : 1) * 100;
238+
const slowPathNonMatches =
239+
(nonMatches ? 1.0 - timing[SelectorTimingsKey.FastRejectCount] / nonMatches : 0) * 100;
238240
const styleSheetId = timing[SelectorTimingsKey.StyleSheetId] as Protocol.CSS.StyleSheetId;
239241
let linkData = '';
240242
const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
@@ -252,7 +254,7 @@ export class TimelineSelectorStatsView extends UI.Widget.VBox {
252254
timing[SelectorTimingsKey.Elapsed] / 1000.0,
253255
timing[SelectorTimingsKey.MatchAttempts],
254256
timing[SelectorTimingsKey.MatchCount],
255-
rejectPercentage,
257+
slowPathNonMatches,
256258
timing[SelectorTimingsKey.Selector],
257259
linkData,
258260
].join('\t'));

test/e2e/helpers/datagrid-helpers.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,23 @@ export async function getDataGridRows(
2929
return await Promise.all(handlers.map(handler => devToolsPage.$$('td[jslog]:not(.hidden)', handler)));
3030
}
3131

32+
export async function getDataGridColumnNames(
33+
root?: ElementHandle<Node>,
34+
devToolsPage: DevToolsPage = getBrowserAndPagesWrappers().devToolsPage): Promise<String[]> {
35+
const columnNames: String[] = [];
36+
const dataGrid = !root ? await devToolsPage.waitFor('devtools-data-grid') : root;
37+
38+
const columnNodes = await dataGrid.$$('pierce/[role="columnheader"]');
39+
for (const column of columnNodes) {
40+
const text = await column.evaluate(x => {
41+
return (x as HTMLElement).innerText || '';
42+
});
43+
columnNames.push(text);
44+
}
45+
46+
return columnNames;
47+
}
48+
3249
export async function getDataGrid(root?: ElementHandle, devToolsPage = getBrowserAndPagesWrappers().devToolsPage) {
3350
const dataGrid = await devToolsPage.waitFor('devtools-data-grid', root);
3451
if (!dataGrid) {

test/e2e/resources/performance/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ copy_to_gen("performance") {
1818
"selectorStats/page-with-style-perf-test.html",
1919
"selectorStats/page-with-style.css",
2020
"selectorStats/page-with-style.html",
21+
"selectorStats/slow-path-non-match.html",
2122
"timeline/treeView-test-trace.json",
2223
"timeline/web.dev-trace.json.gz",
2324
"wasm/mainWasm_profile.json",
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<html>
2+
3+
<head>
4+
<style>
5+
.parent .last-child {
6+
color: red;
7+
}
8+
</style>
9+
</head>
10+
11+
<body>
12+
<div>
13+
<span class="last-child">1</span>
14+
<span class="last-child">2</span>
15+
<span class="last-child">3</span>
16+
<span class="last-child">4</span>
17+
</div>
18+
<span class="parent">
19+
<span class="last-child">5</span>
20+
</span>
21+
</body>
22+
23+
</html>

test/e2e_non_hosted/performance/selector-stats-tracing_test.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@
44

55
import {assert} from 'chai';
66

7-
import {getDataGrid, getDataGridRows, getInnerTextOfDataGridCells} from '../../e2e/helpers/datagrid-helpers.js';
7+
import {
8+
getDataGrid,
9+
getDataGridColumnNames,
10+
getDataGridRows,
11+
getInnerTextOfDataGridCells
12+
} from '../../e2e/helpers/datagrid-helpers.js';
813
import {
914
enableCSSSelectorStats,
1015
increaseTimeoutForPerfPanel,
@@ -60,6 +65,39 @@ describe('The Performance panel', function() {
6065
await validateSourceTabs(devToolsPage);
6166
});
6267

68+
it('Slow path non matches percentage', async ({devToolsPage, inspectedPage}) => {
69+
await navigateToPerformanceTab('selectorStats/slow-path-non-match', devToolsPage, inspectedPage);
70+
await enableCSSSelectorStats(devToolsPage);
71+
await startRecording(devToolsPage);
72+
73+
// reload the test page to trigger recalc styles events
74+
await inspectedPage.bringToFront();
75+
await inspectedPage.reload();
76+
77+
await devToolsPage.bringToFront();
78+
await stopRecording(devToolsPage);
79+
80+
await navigateToSelectorStatsTab(devToolsPage);
81+
82+
// Sort table by selector
83+
const selectorColumnHeader = await devToolsPage.waitFor('th.selector-column');
84+
await selectorColumnHeader.click();
85+
await devToolsPage.timeout(100);
86+
87+
const dataGrid = await getDataGrid(undefined /* root*/, devToolsPage);
88+
const dataGridText = await getInnerTextOfDataGridCells(
89+
dataGrid, 2 /* expectedNumberOfRows */, false /* matchExactNumberOfRows */, devToolsPage);
90+
91+
const dataGridColumns = await getDataGridColumnNames(undefined /* root*/, devToolsPage);
92+
const selectorColumnIndex = dataGridColumns.indexOf('Selector');
93+
const slowPathColumnIndex = dataGridColumns.indexOf('% of slow-path non-matches');
94+
95+
// get the slow path non match percentage for the selector '.parent .last-child'
96+
// 1st row is '(Totals for all selectors)', 2nd row is the selector '.parent .last-child'
97+
assert.strictEqual(dataGridText[1][selectorColumnIndex], '.parent .last-child');
98+
assert.strictEqual(dataGridText[1][slowPathColumnIndex], '0.0');
99+
});
100+
63101
it('Includes a selector stats table in recalculate style events', async ({devToolsPage, inspectedPage}) => {
64102
await cssSelectorStatsRecording('empty', devToolsPage, inspectedPage);
65103

0 commit comments

Comments
 (0)