blob: ddc819d5c1688f5eeff1179b3a608a10086ac558 [file] [log] [blame] [edit]
// Copyright (C) 2025 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import m from 'mithril';
import {AsyncLimiter} from '../../base/async_limiter';
import {
AreaSelection,
AreaSelectionTab,
areaSelectionsEqual,
} from '../../public/selection';
import {Trace} from '../../public/trace';
import {Button} from '../../widgets/button';
import {EmptyState} from '../../widgets/empty_state';
import {Spinner} from '../../widgets/spinner';
import {BenchmarkResult, runBenchmarks} from './benchmark';
function formatApproach(approach: string): string {
switch (approach) {
case 'uri_string':
return 'URI String';
case 'track_index':
return 'Track Index';
case 'groupid':
return 'GroupID';
case 'no_lineage':
return 'No Lineage';
default:
return approach;
}
}
function formatMs(ms: number): string {
return ms.toFixed(2);
}
interface BenchmarkTableAttrs {
results: BenchmarkResult[];
}
class BenchmarkTable implements m.ClassComponent<BenchmarkTableAttrs> {
view({attrs}: m.CVnode<BenchmarkTableAttrs>) {
const {results} = attrs;
return m(
'table.pf-benchmark-table',
{
style: {
width: '100%',
borderCollapse: 'collapse',
fontFamily: 'monospace',
fontSize: '12px',
},
},
[
m(
'thead',
m('tr', [
m('th', {style: {textAlign: 'left', padding: '8px'}}, 'Approach'),
m('th', {style: {textAlign: 'right', padding: '8px'}}, 'Tracks'),
m('th', {style: {textAlign: 'right', padding: '8px'}}, 'Rows'),
m(
'th',
{style: {textAlign: 'right', padding: '8px'}},
'Build (ms)',
),
m(
'th',
{style: {textAlign: 'right', padding: '8px'}},
'Execute (ms)',
),
m(
'th',
{style: {textAlign: 'right', padding: '8px'}},
'Total (ms)',
),
]),
),
m(
'tbody',
results.map((r) =>
m('tr', {style: {borderTop: '1px solid var(--sys-border)'}}, [
m('td', {style: {padding: '8px'}}, formatApproach(r.approach)),
m(
'td',
{style: {textAlign: 'right', padding: '8px'}},
r.trackCount,
),
m(
'td',
{style: {textAlign: 'right', padding: '8px'}},
r.rowCount.toLocaleString(),
),
m(
'td',
{style: {textAlign: 'right', padding: '8px'}},
formatMs(r.queryBuildTimeMs),
),
m(
'td',
{style: {textAlign: 'right', padding: '8px'}},
formatMs(r.queryExecuteTimeMs),
),
m(
'td',
{
style: {
textAlign: 'right',
padding: '8px',
fontWeight: 'bold',
},
},
formatMs(r.totalTimeMs),
),
]),
),
),
],
);
}
}
export function createBenchmarkTab(trace: Trace): AreaSelectionTab {
const limiter = new AsyncLimiter();
let currentSelection: AreaSelection | undefined;
let results: BenchmarkResult[] | undefined;
let isLoading = false;
return {
id: 'aggregation_benchmark',
name: 'Benchmark',
render(selection: AreaSelection) {
const selectionChanged =
currentSelection === undefined ||
!areaSelectionsEqual(selection, currentSelection);
if (selectionChanged) {
currentSelection = selection;
results = undefined;
}
const runBenchmark = () => {
limiter.schedule(async () => {
isLoading = true;
results = undefined;
results = await runBenchmarks(trace.engine, selection.tracks);
isLoading = false;
});
};
if (isLoading) {
return {
isLoading: true,
content: m(
EmptyState,
{
icon: 'speed',
title: 'Running benchmark...',
},
m(Spinner, {easing: true}),
),
};
}
if (!results) {
return {
isLoading: false,
content: m('div', {style: {padding: '16px', textAlign: 'center'}}, [
m('p', `Selected ${selection.tracks.length} tracks`),
m(Button, {
label: 'Run Benchmark',
onclick: runBenchmark,
}),
]),
};
}
return {
isLoading: false,
content: m('div', {style: {padding: '16px'}}, [
m('h3', {style: {margin: '0 0 16px 0'}}, 'Benchmark Results'),
m(BenchmarkTable, {results}),
m(
'div',
{style: {marginTop: '16px'}},
m(Button, {
label: 'Re-run Benchmark',
onclick: runBenchmark,
}),
),
]),
};
},
};
}