Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up the classes that handle run results, and correctly import JSON files with multiple runs #77

Merged
merged 5 commits into from
Feb 5, 2025

Conversation

smfr
Copy link
Contributor

@smfr smfr commented Sep 16, 2024

This series of commits clean up ResultsDashboard (now ScoreCalculator), and adds RunData.

It also adds unit tests for data import.

Fix a bug where we failed to pass the event to a the “benchmark-options” radio changed handler.
Add test-data-import.js which tests creating a `ResultsDashboard` with imported JSON,
which is the JSON produced by running the test via the browser. The test validates that
the results analysis produces output data in the correct format, but does not validate
the score calculation.

To make the unit test run faster, we make `bootstrapIterations` be configurable via
an option. The unit test sets this to a small value.

Fix a bug that prevented the JSON results dialog being shown by using arrow functions
in `BenchmarkController` so that `this` is correctly bound.
`BenchmarkRunnerClient.results` becomes `BenchmarkRunnerClient.scoreCalculator`.

Add a public getter for `targetFrameRate`.
This class encapsulates the tuple of options, version and run data. ScoreCalculator can then store this in `runData`, replacing the separate storage of `options`, `version` and the confusingly named `_iterationsSamplers`.
@smfr smfr requested review from shallawa and rniwa September 16, 2024 23:54
@@ -704,38 +691,38 @@ class DebugBenchmarkController extends BenchmarkController {
this.addedKeyEvent = true;
}

var dashboard = this.runnerClient.results;
if (dashboard.options["controller"] == "ramp")
var scoreCalculator = this.runnerClient.scoreCalculator;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const works.

"% / +" + ((dashboard.scoreUpperBound / score - 1) * 100).toFixed(2) + "%";
var fps = dashboard._systemFrameRate;
sectionsManager.setSectionVersion("results", dashboard.version);
var score = scoreCalculator.score;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not const or let for these?

RunData.#migrateImportedData(singleRunData);

if (!singleRunData.data instanceof Array) {
console.log('Imported singleRunData.data is not an array. Bailing');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not console.error?

static resultsDataFromBenchmarkRunnerData(benchmarkData)
{
if (!benchmarkData instanceof Array) {
console.log('Imported benchmarkData is not an array. Bailing');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto and ditto for the rest of console.log in this file.

}

let runData = [];
for (let run of benchmarkData) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use const instead?

class ScoreCalculator {
constructor(runData)
{
this._runData = runData;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use private member variables?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole codebase needs a switch to private variables. For now I was following existing code.

constructor(element, headers)
{
this.element = element;
this._headers = headers;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use a private member variable?

this.element = element;
this._headers = headers;

this._flattenedHeaders = [];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.

this._headers = headers;

this._flattenedHeaders = [];
this._headers.forEach(function(header) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.

function findRegression(series, profile) {
var minIndex = Math.round(.025 * series.length);
var maxIndex = Math.round(.975 * (series.length - 1));
var minComplexity = series.getFieldInDatum(minIndex, Strings.json.complexity);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of keep accessing Strings.json.complexity over and over, can we just store in a local const variable and use that?


// Convert these samples into SampleData objects if needed
[Strings.json.complexity, Strings.json.controller].forEach(function(seriesName) {
var series = samples[seriesName];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const?

const desiredFrameLength = 1000 / this._targetFrameRate;

function findRegression(series, profile) {
var minIndex = Math.round(.025 * series.length);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like these could all be const. If not, let?

@smfr smfr force-pushed the handle-multiple-iteration-data branch from 13dce51 to dd5fe97 Compare February 3, 2025 22:35
@@ -60,6 +60,7 @@ var Strings = {
scoreUpperBound: "scoreUpperBound",
fps: "fps",
bootstrap: "bootstrap",
bootstrapIterations: "bootstrapIterations",
Copy link
Member

@rniwa rniwa Feb 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this side table really useful? It seems like we can just hard-code these strings where it's used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really know why this strings table was used. I agree it's unwieldy.


_addHeader()
{
var thead = Utilities.createElement("thead", {}, this.element);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use const/let here as well?


_addEmptyRow()
{
var row = Utilities.createElement("tr", {}, this.tbody);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use const/let here as well?


_addTest(testName, testResult, options)
{
var row = Utilities.createElement("tr", {}, this.tbody);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use const/let here as well?


_addIteration(iterationResult, iterationData, options)
{
var testsResults = iterationResult[Strings.json.results.tests];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use const/let here as well?

@@ -0,0 +1,137 @@
/*
* Copyright (C) 2024 Apple Inc. All rights reserved.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2024-2025?

// Check that we have results, not their exact values.
const firstResults = this.results[0];

expect(typeof firstResults[Strings.json.score]).to.be('number');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we specify some small range for the actual value?

When importing a file which is the result of a benchmark run with multiple iterations (which contains a `debugOutput` key), store all the runs in `RunData`.

Add static methods to `RunData` to handle both types of JSON, and move the data migration code there.

The score computation already correctly computed the arithmetic mean of the runs.

The code already correctly built the results table for multiple runs.
@smfr smfr force-pushed the handle-multiple-iteration-data branch from dd5fe97 to 94f4fa7 Compare February 5, 2025 23:01
@smfr smfr merged commit fc28b62 into WebKit:main Feb 5, 2025
@smfr smfr deleted the handle-multiple-iteration-data branch February 5, 2025 23:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants