Skip to content

Commit 82c855f

Browse files
Merge branch 'main' into cleanup/shared
2 parents 67d1e56 + f1eddfe commit 82c855f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1783
-215
lines changed

experimental/README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Experimental Workloads
2+
3+
Experimental Speedometer workloads can be used for workloads that do not
4+
directly meet the criteria for official workloads:
5+
6+
- Incubation of new workloads
7+
- Edge-cases that don't work well in the official setup
8+
- Testing features that aren't fully shipped in all browsers
9+
10+
The main purpose is to have a space for low-friction contributions to
11+
experiment with new ideas. There is no explicit requirement to promote
12+
experimental workloads to the official selection.
13+
14+
## Review Process
15+
16+
Contributions to experimental workloads can be merged with the approval from a
17+
single vendor.
18+
19+
This is the opposite from official workloads that are scored, where we require
20+
approval from all vendors.
21+
22+
## Requirements
23+
24+
- Stored in the `/experimental` folder
25+
- Disabled by default and thus do not contribute to the official score
26+
- Use the `experimental` tag in the Suites declaration
27+
28+
## Hosting
29+
30+
Experimental workloads will not be officially hosted on Speedometer releases on
31+
<https://browserbench.org>, unlike the official workloads.

index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ <h2>Non-standard Parameters</h2>
100100
</div>
101101
<div class="aggregated-metric-result">
102102
<h2>Aggregate Metric</h2>
103-
<div id="geomean-chart"></div>
103+
<div id="aggregate-chart"></div>
104104
<h2>Test Metrics Overview</h2>
105105
<div id="tests-chart"></div>
106106
</div>

resources/benchmark-runner.mjs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Metric } from "./metric.mjs";
22
import { params } from "./shared/params.mjs";
3+
import { forceLayout } from "./shared/helpers.mjs";
34
import { SUITE_RUNNER_LOOKUP } from "./suite-runner.mjs";
45

56
const performance = globalThis.performance;
@@ -30,8 +31,9 @@ class Page {
3031
}
3132

3233
layout() {
33-
const body = this._frame.contentDocument.body.getBoundingClientRect();
34-
this.layout.e = document.elementFromPoint((body.width / 2) | 0, (body.height / 2) | 0);
34+
const body = this._frame ? this._frame.contentDocument.body : document.body;
35+
const value = forceLayout(body, params.layoutMode);
36+
body._leakedLayoutValue = value; // Prevent dead code elimination.
3537
}
3638

3739
async waitForElement(selector) {
@@ -407,9 +409,8 @@ export class BenchmarkRunner {
407409
const suites = await this._prepareAllSuites();
408410
try {
409411
for (const suite of suites) {
410-
if (suite.disabled)
412+
if (!suite.enabled)
411413
continue;
412-
413414
try {
414415
await this._appendFrame();
415416
this._page = new Page(this._frame);
@@ -468,10 +469,10 @@ export class BenchmarkRunner {
468469

469470
_appendIterationMetrics() {
470471
const getMetric = (name, unit = "ms") => this._metrics[name] || (this._metrics[name] = new Metric(name, unit));
471-
const iterationTotalMetric = (i) => {
472+
const iterationMetric = (i, name) => {
472473
if (i >= params.iterationCount)
473474
throw new Error(`Requested iteration=${i} does not exist.`);
474-
return getMetric(`Iteration-${i}-Total`);
475+
return getMetric(`Iteration-${i}-${name}`);
475476
};
476477

477478
const collectSubMetrics = (prefix, items, parent) => {
@@ -496,20 +497,34 @@ export class BenchmarkRunner {
496497
// Prepare all iteration metrics so they are listed at the end of
497498
// of the _metrics object, before "Total" and "Score".
498499
for (let i = 0; i < this._iterationCount; i++)
499-
iterationTotalMetric(i).description = `Test totals for iteration ${i}`;
500+
iterationMetric(i, "Total").description = `Test totals for iteration ${i}`;
500501
getMetric("Geomean", "ms").description = "Geomean of test totals";
501502
getMetric("Score", "score").description = "Scaled inverse of the Geomean";
503+
if (params.measurePrepare)
504+
getMetric("Prepare", "ms").description = "Geomean of workload prepare times";
502505
}
503506

504507
const geomean = getMetric("Geomean");
505-
const iterationTotal = iterationTotalMetric(geomean.length);
508+
const iteration = geomean.length;
509+
const iterationTotal = iterationMetric(iteration, "Total");
506510
for (const results of Object.values(iterationResults))
507511
iterationTotal.add(results.total);
508512
iterationTotal.computeAggregatedMetrics();
509513
geomean.add(iterationTotal.geomean);
510514
getMetric("Score").add(geomeanToScore(iterationTotal.geomean));
511515

516+
if (params.measurePrepare) {
517+
const iterationPrepare = iterationMetric(iteration, "Prepare");
518+
for (const results of Object.values(iterationResults))
519+
iterationPrepare.add(results.prepare);
520+
iterationPrepare.computeAggregatedMetrics();
521+
const prepare = getMetric("Prepare");
522+
prepare.add(iterationPrepare.geomean);
523+
}
524+
512525
for (const metric of Object.values(this._metrics))
513526
metric.computeAggregatedMetrics();
514527
}
528+
529+
_initializeMetrics() {}
515530
}

resources/developer-mode.mjs

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Suites, Tags } from "./tests.mjs";
2-
import { params } from "./shared/params.mjs";
2+
import { params, LAYOUT_MODES } from "./shared/params.mjs";
33

44
export function createDeveloperModeContainer() {
55
const container = document.createElement("div");
@@ -18,10 +18,12 @@ export function createDeveloperModeContainer() {
1818
const settings = document.createElement("div");
1919
settings.className = "settings";
2020
settings.append(createUIForIterationCount());
21+
settings.append(createUIForMeasurePrepare());
2122
settings.append(createUIForWarmupSuite());
2223
settings.append(createUIForWarmupBeforeSync());
2324
settings.append(createUIForSyncStepDelay());
2425
settings.append(createUIForAsyncSteps());
26+
settings.append(createUIForLayoutMode());
2527

2628
content.append(document.createElement("hr"));
2729
content.append(settings);
@@ -46,6 +48,12 @@ function createUIForWarmupSuite() {
4648
});
4749
}
4850

51+
function createUIForMeasurePrepare() {
52+
return createCheckboxUI("Measure Prepare", params.measurePrepare, (isChecked) => {
53+
params.measurePrepare = isChecked;
54+
});
55+
}
56+
4957
function createUIForAsyncSteps() {
5058
return createCheckboxUI("Use Async Steps", params.useAsyncSteps, (isChecked) => {
5159
params.useAsyncSteps = isChecked;
@@ -107,12 +115,37 @@ function createTimeRangeUI(labelText, paramKey, unit = "ms", min = 0, max = 1000
107115
return label;
108116
}
109117

118+
function createUIForLayoutMode() {
119+
return createSelectUI("Force layout mode", params.layoutMode, LAYOUT_MODES, (value) => {
120+
params.layoutMode = value;
121+
});
122+
}
123+
124+
function createSelectUI(labelValue, initialValue, choices, paramsUpdateCallback) {
125+
const select = document.createElement("select");
126+
select.onchange = () => {
127+
paramsUpdateCallback(select.value);
128+
updateURL();
129+
};
130+
131+
choices.forEach((choice) => {
132+
const option = new Option(choice, choice);
133+
select.add(option);
134+
});
135+
select.value = initialValue;
136+
137+
const label = document.createElement("label");
138+
label.append(span(labelValue), select);
139+
140+
return label;
141+
}
142+
110143
function createUIForSuites() {
111144
const control = document.createElement("nav");
112145
control.className = "suites";
113146
const checkboxes = [];
114147
const setSuiteEnabled = (suiteIndex, enabled) => {
115-
Suites[suiteIndex].disabled = !enabled;
148+
Suites[suiteIndex].enabled = enabled;
116149
checkboxes[suiteIndex].checked = enabled;
117150
};
118151

@@ -124,9 +157,9 @@ function createUIForSuites() {
124157
const checkbox = document.createElement("input");
125158
checkbox.id = suite.name;
126159
checkbox.type = "checkbox";
127-
checkbox.checked = !suite.disabled;
160+
checkbox.checked = suite.enabled;
128161
checkbox.onchange = () => {
129-
suite.disabled = !checkbox.checked;
162+
suite.enabled = checkbox.checked;
130163
updateURL();
131164
};
132165
checkboxes.push(checkbox);
@@ -219,12 +252,13 @@ function createSuitesTagsButton(setSuiteEnabled) {
219252

220253
function createUIForRun() {
221254
const stepTestButton = document.createElement("button");
222-
stepTestButton.textContent = "Step Test \u23EF";
255+
stepTestButton.className = "step-button";
256+
stepTestButton.innerHTML = "Step Test<span>\u23EF</span>";
223257
stepTestButton.onclick = (event) => {
224258
globalThis.benchmarkClient.step();
225259
};
226260
const startTestButton = document.createElement("button");
227-
startTestButton.textContent = "Start Test \u23F5";
261+
startTestButton.innerHTML = "Start Test<span>\u23F5</span>";
228262
startTestButton.onclick = (event) => {
229263
globalThis.benchmarkClient.start();
230264
};
@@ -241,17 +275,17 @@ function updateParamsSuitesAndTags() {
241275

242276
// If less than all suites are selected then change the URL "Suites" GET parameter
243277
// to comma separate only the selected
244-
const selectedSuites = Suites.filter((suite) => !suite.disabled);
278+
const selectedSuites = Suites.filter((suite) => suite.enabled);
245279
if (!selectedSuites.length)
246280
return;
247281

248282
// Try finding common tags that would result in the current suite selection.
249283
let commonTags = new Set(selectedSuites[0].tags);
250284
for (const suite of Suites) {
251-
if (suite.disabled)
252-
suite.tags.forEach((tag) => commonTags.delete(tag));
253-
else
285+
if (suite.enabled)
254286
commonTags = new Set(suite.tags.filter((tag) => commonTags.has(tag)));
287+
else
288+
suite.tags.forEach((tag) => commonTags.delete(tag));
255289
}
256290
if (selectedSuites.length > 1 && commonTags.size)
257291
params.tags = [...commonTags];

resources/main.css

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,15 +287,28 @@ button,
287287
}
288288

289289
.developer-mode-content button {
290+
font-size: 16px;
290291
background: white;
291292
flex: auto;
292293
padding: 4px;
293294
appearance: button;
294295
border: 2px solid rgba(255, 255, 255, 0.5);
295296
border-radius: 10px;
296297
}
298+
299+
.developer-mode-content button span {
300+
padding-left: 10px;
301+
font-size: 1.5em;
302+
line-height: 10px;
303+
position: relative;
304+
top: 2px;
305+
}
306+
297307
.developer-mode-content .tag {
298308
border-radius: 100vh;
309+
}
310+
311+
.developer-mode-content .step-button {
299312
color: white;
300313
background: rgba(255, 255, 255, 0.1);
301314
}
@@ -518,6 +531,8 @@ section#details .non-standard-params {
518531
display: none;
519532
text-align: center;
520533
margin-bottom: 2em;
534+
/* Increase contrast a bit */
535+
filter: brightness(1.5);
521536
}
522537

523538
section#details .non-standard-params h2 {
@@ -547,6 +562,9 @@ section#details .non-standard-params h2 {
547562
#non-standard-params-table tbody td {
548563
padding: 0.1em 0.3em;
549564
}
565+
#non-standard-params-table tbody td:nth-child(2) {
566+
color: var(--highlight);
567+
}
550568

551569
section#details .all-metric-results {
552570
flex: auto;

resources/main.mjs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ class MainBenchmarkClient {
6666
if (this._isRunning)
6767
return false;
6868

69-
if (Suites.every((suite) => suite.disabled)) {
69+
const enabledSuites = Suites.filter((suite) => suite.enabled);
70+
const totalSuitesCount = enabledSuites.length;
71+
if (totalSuitesCount === 0) {
7072
const message = `No suites selected - "${params.suites}" does not exist.`;
7173
alert(message);
7274
console.error(
@@ -75,7 +77,6 @@ class MainBenchmarkClient {
7577
"\nValid values:",
7678
Suites.map((each) => each.name)
7779
);
78-
7980
return false;
8081
}
8182
if (!this._isStepping())
@@ -93,8 +94,6 @@ class MainBenchmarkClient {
9394
this._metrics = Object.create(null);
9495
this._isRunning = true;
9596

96-
const enabledSuites = Suites.filter((suite) => !suite.disabled);
97-
const totalSuitesCount = enabledSuites.length;
9897
this.stepCount = params.iterationCount * totalSuitesCount;
9998
this._progressCompleted.max = this.stepCount;
10099
this.suitesCount = enabledSuites.length;
@@ -250,8 +249,11 @@ class MainBenchmarkClient {
250249
const trackHeight = 24;
251250
document.documentElement.style.setProperty("--metrics-line-height", `${trackHeight}px`);
252251
const plotWidth = (params.viewport.width - 120) / 2;
253-
document.getElementById("geomean-chart").innerHTML = renderMetricView({
254-
metrics: [metrics.Geomean],
252+
const aggregateMetrics = [metrics.Geomean];
253+
if (params.measurePrepare)
254+
aggregateMetrics.push(metrics.Prepare);
255+
document.getElementById("aggregate-chart").innerHTML = renderMetricView({
256+
metrics: aggregateMetrics,
255257
width: plotWidth,
256258
trackHeight,
257259
renderChildren: false,

resources/shared/benchmark.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export class BenchmarkSuite {
4444
async runAndRecordSuite(params, onProgress) {
4545
const measuredValues = {
4646
tests: {},
47+
prepare: 0,
4748
total: 0,
4849
};
4950
const suiteStartLabel = `suite-${this.name}-start`;

resources/shared/helpers.mjs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,15 @@ export function getAllElements(selector, path = [], lookupStartNode = document)
4141
return elements;
4242
}
4343

44-
/**
45-
* Forces a reflow/layout of the document by requesting a DOM property that triggers it.
46-
* This can be useful for ensuring that styles and positions are up-to-date before
47-
* performing measurements or animations. It also returns an element from the center of the viewport.
48-
*
49-
* @returns {Element|null} An element at the center of the viewport, or null if no element is there.
50-
*/
51-
export function forceLayout() {
52-
const rect = document.body.getBoundingClientRect();
53-
const e = document.elementFromPoint((rect.width / 2) | 0, (rect.height / 2) | 0);
54-
return e;
44+
export function forceLayout(body, layoutMode = "getBoundingRectAndElementFromPoint") {
45+
body ??= document.body;
46+
const rect = body.getBoundingClientRect();
47+
switch (layoutMode) {
48+
case "getBoundingRectAndElementFromPoint":
49+
return document.elementFromPoint((rect.width / 2) | 0, (rect.height / 2) | 0);
50+
case "getBoundingClientRect":
51+
return rect.height;
52+
default:
53+
throw Error(`Invalid layoutMode: ${layoutMode}`);
54+
}
5555
}

0 commit comments

Comments
 (0)