From 5e3d97db4c70cb82e817b36c31e524c6de53a578 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Wed, 17 Sep 2025 19:03:12 -0400 Subject: [PATCH 01/13] add config --- .../v3/components/common/StickyBar.tsx | 89 ++++++++++ .../BenchmarkChartSection.tsx | 65 ++++---- .../ComparisonTable.tsx | 78 +++++++++ .../ComparisonTableColumnRendering.tsx | 126 ++++++++++++++ .../ComparisonTableHelpers.tsx | 59 +++++++ ...chmarkTimeSeriesComparisonTableSection.tsx | 154 ++++++++++++++++++ ...nchmarkTimeSeriesComparisonTableSlider.tsx | 119 ++++++++++++++ .../components/benchmarkTimeSeries/helper.tsx | 15 ++ .../FanoutBenchmarkTimeSeriesChartSection.tsx | 27 --- .../dataRender/fanout/FanoutComponents.tsx | 47 ++++++ .../v3/configs/teams/compilers/config.ts | 9 + .../v3/configs/utils/fanoutRegistration.tsx | 10 +- .../api_helper/compilers/helpers/general.ts | 1 + .../compilers/helpers/precompute.ts | 6 +- 14 files changed, 744 insertions(+), 61 deletions(-) create mode 100644 torchci/components/benchmark/v3/components/common/StickyBar.tsx rename torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/{components => }/BenchmarkChartSection.tsx (51%) create mode 100644 torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTable.tsx create mode 100644 torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableColumnRendering.tsx create mode 100644 torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableHelpers.tsx create mode 100644 torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx create mode 100644 torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSlider.tsx delete mode 100644 torchci/components/benchmark/v3/components/dataRender/fanout/FanoutBenchmarkTimeSeriesChartSection.tsx create mode 100644 torchci/components/benchmark/v3/components/dataRender/fanout/FanoutComponents.tsx diff --git a/torchci/components/benchmark/v3/components/common/StickyBar.tsx b/torchci/components/benchmark/v3/components/common/StickyBar.tsx new file mode 100644 index 0000000000..3af46ab238 --- /dev/null +++ b/torchci/components/benchmark/v3/components/common/StickyBar.tsx @@ -0,0 +1,89 @@ +import { Box } from "@mui/system"; +import { useEffect, useRef, useState } from "react"; + +export type StickyBarProps = { + children: React.ReactNode; + height?: number; + offset: number; + zIndex?: number; + onMount?: (h: number) => void; + onUnmount?: (h: number) => void; + /** Horizontal alignment of content inside the bar */ + align?: "left" | "center" | "right"; + /** Should children keep their natural width ("fit") or stretch ("full") */ + contentMode?: "fit" | "full"; +}; + +export const StickyBar: React.FC = ({ + children, + height = 48, + offset, + zIndex = 900, + onMount, + onUnmount, + align = "left", + contentMode = "fit", +}) => { + const ref = useRef(null); + const [isSticky, setIsSticky] = useState(false); + + // Let parent know about mount/unmount (for stacking offset logic) + useEffect(() => { + onMount?.(height); + return () => onUnmount?.(height); + }, [height, onMount, onUnmount]); + + // IntersectionObserver: sticky only after scroll + useEffect(() => { + const observer = new IntersectionObserver(([entry]) => { + setIsSticky(!entry.isIntersecting); + }); + if (ref.current) observer.observe(ref.current); + return () => observer.disconnect(); + }, []); + + const justify = + align === "center" + ? "center" + : align === "right" + ? "flex-end" + : "flex-start"; + + return ( + <> + {/* Sentinel keeps layout height stable */} +
+ {/* Outer bar: full width, sticky */} + + {/* Inner container: controls how children size themselves */} + *": { flex: "0 0 auto", minWidth: "auto" }, // don’t stretch children + }} + > + {children} + + +
+ + ); +}; diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkChartSection.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/BenchmarkChartSection.tsx similarity index 51% rename from torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkChartSection.tsx rename to torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/BenchmarkChartSection.tsx index 28776f71e5..f52ce6848d 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkChartSection.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/BenchmarkChartSection.tsx @@ -1,13 +1,13 @@ import { Paper, Typography } from "@mui/material"; import { Box } from "@mui/system"; import { useMemo } from "react"; +import BenchmarkTimeSeriesChartGroup from "./components/BenchmarkTimeSeriesChartGroup"; import { BenchmarkChartSectionConfig, BenchmarkTimeSeriesInput, makeGroupKeyAndLabel, passesFilter, -} from "../helper"; -import BenchmarkTimeSeriesChartGroup from "./BenchmarkTimeSeriesChartGroup"; +} from "./helper"; const styles = { container: { @@ -52,7 +52,9 @@ export default function BenchmarkChartSection({ gi, chartSectionConfig.groupByFields ); - if (!m.has(key)) m.set(key, { key, labels, items: [] }); + if (!m.has(key)) { + m.set(key, { key, labels, items: [] }); + } m.get(key)!.items.push(s); } return m; @@ -63,34 +65,37 @@ export default function BenchmarkChartSection({ } return ( - - {Array.from(groupMap.entries()).map(([key, data]) => { - if (!data) return null; - const op = chartSectionConfig.chartGroup?.renderOptions; - const title = data.labels.join(" "); + + Time Series Chart Section + + {Array.from(groupMap.entries()).map(([key, data]) => { + if (!data) return null; + const op = chartSectionConfig.chartGroup?.renderOptions; + const title = data.labels.join(" "); - let renderOptions = chartSectionConfig.chartGroup?.renderOptions; - if (op && op.pass_section_title) { - renderOptions = { - ...renderOptions, - titleSuffix: `/${title}`, - }; - } - return ( - - - {title.toUpperCase()} - { - onChange?.(payload); - }} - /> - - - ); - })} + let renderOptions = chartSectionConfig.chartGroup?.renderOptions; + if (op && op.pass_section_title) { + renderOptions = { + ...renderOptions, + titleSuffix: `/${title}`, + }; + } + return ( + + + {title.toUpperCase()} + { + onChange?.(payload); + }} + /> + + + ); + })} + ); } diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTable.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTable.tsx new file mode 100644 index 0000000000..76bcd21b8e --- /dev/null +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTable.tsx @@ -0,0 +1,78 @@ +import { DataGrid, GridColDef, GridRowModel } from "@mui/x-data-grid"; +import { useMemo } from "react"; +import { ComparisonTableConfig } from "../../../helper"; +import { getComparisionTableConlumnRendering } from "./ComparisonTableColumnRendering"; +import { + getComparisonTableRowDefinition, + SnapshotRow, +} from "./ComparisonTableHelpers"; + +export function ComparisonTable({ + data, + lWorkflowId, + rWorkflowId, + config, + columnOrder, + title = "Group", +}: { + data: SnapshotRow[]; + lWorkflowId: string | null; + rWorkflowId: string | null; + config: ComparisonTableConfig; + columnOrder?: string[]; // optional preferred ordering of columns + title?: string; +}) { + // group raw data into rows, each row contains all values across workflowIds + const rows: GridRowModel[] = useMemo(() => { + return getComparisonTableRowDefinition(config, data); + }, [data, config.nameKey]); + + // union of all column ids + const allColumns = useMemo(() => { + const s = new Set(); + rows.forEach((r) => + Object.values(r.byWorkflow).forEach((cols) => { + Object.keys(cols ?? {}).forEach((k) => s.add(k)); + }) + ); + const auto = Array.from(s).sort(); + if (!columnOrder || columnOrder.length === 0) return auto; + const head = columnOrder.filter((c) => s.has(c)); + const tail = auto.filter((c) => !head.includes(c)); + return [...head, ...tail]; + }, [rows, columnOrder]); + + // Form the columns + const columns: GridColDef[] = useMemo( + () => + getComparisionTableConlumnRendering(allColumns, lWorkflowId, rWorkflowId), + [allColumns, lWorkflowId, rWorkflowId, title] + ); + + return ( + r.id} + sx={{ + "& .MuiDataGrid-cell": { + py: 0, // less vertical padding + fontSize: "0.75rem", + }, + "& .MuiDataGrid-columnHeaders": { + minHeight: 32, + maxHeight: 32, + lineHeight: "32px", + fontSize: "0.75rem", + }, + "& .MuiDataGrid-row": { + minHeight: 32, + maxHeight: 32, + }, + }} + hideFooter + /> + ); +} diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableColumnRendering.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableColumnRendering.tsx new file mode 100644 index 0000000000..cf29318d23 --- /dev/null +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableColumnRendering.tsx @@ -0,0 +1,126 @@ +import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; +import { IconButton, Tooltip, Typography } from "@mui/material"; +import { Box } from "@mui/system"; +import { + GridColDef, + GridRenderCellParams, + GridRowModel, +} from "@mui/x-data-grid"; +import { asNumber, valOf } from "./ComparisonTableHelpers"; + +/** + * + * @param allColumns + * @param lWorkflowId + * @param rWorkflowId + * @returns + */ +export function getComparisionTableConlumnRendering( + columnsFields: string[], + lWorkflowId: string | null, + rWorkflowId: string | null +): GridColDef[] { + const nameCol: GridColDef = { + field: "name", + headerName: "Name", + flex: 1.2, + sortable: false, + filterable: false, + renderCell: (p) => {p.row.name}, + }; + const metricCols: GridColDef[] = columnsFields.map((field) => ({ + field, + headerName: field, + flex: 1, + sortable: false, + filterable: false, + renderCell: (params: GridRenderCellParams) => ( + + ), + })); + const labelCol: GridColDef = { + field: "label", + headerName: "Label", + flex: 1.2, + sortable: false, + filterable: false, + renderCell: (p) => ( + + + + + + ), + }; + return [nameCol, ...metricCols, labelCol]; +} + +/** Colors */ +const VIOLATE_RULE_COLOR = "#ffebee"; // red[50] +const IMPROVEMENT_COLOR = "#e8f5e9"; // green[50] + +/** + * + * @returns + */ +export function ComparisonTableValueCell({ + field, + row, + lWorkflowId, + rWorkflowId, +}: { + field: string; + row: GridRowModel; + lWorkflowId: string | null; + rWorkflowId: string | null; +}) { + // If your value is directly rows[col], drop `.data?.[0]` + const L = valOf( + lWorkflowId + ? row.byWorkflow[lWorkflowId]?.[field]?.data?.[0] ?? + row.byWorkflow[lWorkflowId]?.[field] + : undefined + ); + const R = valOf( + rWorkflowId + ? row.byWorkflow[rWorkflowId]?.[field]?.data?.[0] ?? + row.byWorkflow[rWorkflowId]?.[field] + : undefined + ); + + const fmt = (v: any) => + v == null ? "—" : typeof v === "number" ? Number(v).toFixed(2) : String(v); + const ln = asNumber(L); + const rn = asNumber(R); + const d = ln != null && rn != null ? rn - ln : null; + const dStr = d == null ? "—" : `${d >= 0 ? "+" : ""}${Number(d.toFixed(3))}`; + + const bg = + d == null || d === 0 + ? undefined + : d > 0 + ? IMPROVEMENT_COLOR + : VIOLATE_RULE_COLOR; + + const text = + L == null && R == null + ? "N/A" + : L == null + ? `N/A→${fmt(R)}` + : R == null + ? `${fmt(L)}→N/A` + : L === R + ? `${fmt(L)}` + : `${fmt(L)}→${fmt(R)} (${dStr})`; + + return ( + + {text} + + ); +} diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableHelpers.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableHelpers.tsx new file mode 100644 index 0000000000..ef2814ad96 --- /dev/null +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableHelpers.tsx @@ -0,0 +1,59 @@ +import { ComparisonTableConfig } from "../../../helper"; + +type GridRowModel = { + id: string; + label: string; + name: string; + metric: string; + byWorkflow: Record; + sampleInfo: any; +}; + +// used when find unique rows +const EXCLUDE_KEYS = ["workflow_id", "commit", "branch"]; +function getGroupKeyAndLabel(gi: any) { + const keys = Object.keys(gi ?? {}) + .filter((k) => !EXCLUDE_KEYS.includes(k)) + .sort(); + const key = keys.map((k) => `${k}=${String(gi?.[k])}`).join("|"); + const label = keys.map((k) => String(gi?.[k])).join(" · "); + return { key, label, metric: String(gi?.metric ?? "") }; +} + +/** Input types (your shape) */ +export type RowCellObj = { + value: number | string | null | undefined; + [k: string]: any; +}; +export type RowColumns = Record; +export type SnapshotRow = { group_info: any; rows: RowColumns }; + +/** Helpers */ +export const asNumber = (v: unknown) => (typeof v === "number" ? v : undefined); +export const valOf = (cell?: RowCellObj) => (cell ? cell.value : undefined); + +export function getComparisonTableRowDefinition( + config: ComparisonTableConfig, + data: any +) { + const m = new Map(); + for (const item of data ?? []) { + const gi = item.group_info ?? {}; + const wf = String(gi?.workflow_id ?? ""); + const { key, label, metric } = getGroupKeyAndLabel(gi); + + const name = config.nameKey ? gi?.[config.nameKey] : label; + if (!m.has(key)) { + m.set(key, { + id: key, + label, + metric, + byWorkflow: {}, + sampleInfo: gi, + name, + }); + } + m.get(key)!.byWorkflow[wf] = item.rows ?? {}; + } + return Array.from(m.values()); +} diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx new file mode 100644 index 0000000000..ed81e20540 --- /dev/null +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx @@ -0,0 +1,154 @@ +import { Divider, Paper, Typography } from "@mui/material"; +import { Box, Grid } from "@mui/system"; +import { StickyBar } from "components/benchmark/v3/components/common/StickyBar"; +import { useMemo, useState } from "react"; +import { + BenchmarkComparisonTableSectionConfig, + makeGroupKeyAndLabel, + passesFilter, +} from "../../helper"; +import { ComparisonTable } from "./BenchmarkTimeSeriesComparisonTable/ComparisonTable"; +import { BenchmarkTimeSeriesComparisonTableSlider } from "./BenchmarkTimeSeriesComparisonTableSlider"; + +const styles = { + container: { + flexGrow: 1, + }, + paper: { + p: 2, + elevation: 2, + borderRadius: 2, + }, +}; + +export default function BenchmarkTimeSeriesComparisonTableSection({ + data = [], + tableSectionConfig, + onChange, +}: { + data?: any[]; + tableSectionConfig: BenchmarkComparisonTableSectionConfig; + onChange?: (payload: any) => void; +}) { + // Sticky bar offset + const [barOffset, setBarOffset] = useState(0); + const handleMount = (h: number) => setBarOffset((prev) => prev + h); + const handleUnmount = (h: number) => setBarOffset((prev) => prev - h); + + // Filter data based on the table config + const filtered = useMemo(() => { + if (!data) { + return []; + } + return data.filter((s) => + passesFilter(s.group_info || {}, tableSectionConfig.filterByFieldValues) + ); + }, [data, tableSectionConfig.filterByFieldValues]); + + // Group data based on the table config + const groupMap = useMemo(() => { + const m = new Map(); + for (const s of filtered) { + const gi = s.group_info || {}; + const { key, labels } = makeGroupKeyAndLabel( + gi, + tableSectionConfig.groupByFields + ); + if (!m.has(key)) m.set(key, { key, labels, items: [] }); + m.get(key)!.items.push(s); + } + return m; + }, [filtered, tableSectionConfig.groupByFields]); + + if (!data || data.length == 0) { + return <>; + } + + // Build a list of workflows for the slider (you can add labels/timestamps here) + const items: any[] = useMemo(() => { + const idMap = new Map(); + for (const d of data) { + const id = String(d.group_info.workflow_id); + const commit = String(d.group_info.commit); + idMap.set(id, { + workflow_id: id, + label: id, + commit: commit, // keep previous commit if any + branch: d.group_info.branch, + }); + } + return Array.from(idMap.values()); + }, [ + filtered, + tableSectionConfig.groupByFields, + tableSectionConfig.filterByFieldValues, + ]); + + // Controlled slider range (indices) + const [range, setRange] = useState<[number, number]>(() => { + const n = items.length; + return n >= 2 ? [0, n - 1] : [0, 0]; + }); + + // Derive L/R workflow IDs from the range + const sortedIds = useMemo( + () => + items + .map((i) => i.workflow_id) + .sort((a, b) => { + const na = /^\d+$/.test(a) ? Number(a) : NaN; + const nb = /^\d+$/.test(b) ? Number(b) : NaN; + return Number.isNaN(na) || Number.isNaN(nb) + ? a.localeCompare(b) + : na - nb; + }), + [items] + ); + + const lWorkflowId = sortedIds[range[0]] ?? null; + const rWorkflowId = sortedIds[range[1]] ?? null; + + return ( + + Time Series Comparison Section + + + + + + {Array.from(groupMap.entries()).map(([key, data]) => { + if (!data) return null; + const title = data.labels.join(" "); + return ( + + + {title.toUpperCase()} + + {lWorkflowId} - {rWorkflowId} + + + + + ); + })} + + + ); +} diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSlider.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSlider.tsx new file mode 100644 index 0000000000..dc93321047 --- /dev/null +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSlider.tsx @@ -0,0 +1,119 @@ +import styled from "@emotion/styled"; +import { Box, Chip, Paper, Slider, Stack, Typography } from "@mui/material"; +import * as React from "react"; +import { useMemo } from "react"; + +export type WorkflowItem = { + workflow_id: string; + commit: string; + [k: string]: any; +}; + +const BenchmarkSlider = styled(Slider)(({ theme }) => ({ + "& .MuiSlider-valueLabelLabel": { + whiteSpace: "pre-line", // <- allow \n + fontSize: 12, + lineHeight: 1.25, + padding: 0, + display: "block", // makes JSX work too + }, +})); + +const shortSha = (id?: string) => + id ? (id.length > 10 ? id.slice(0, 7) : id) : "—"; + +const fmtTs = (ts?: string) => { + if (!ts) return "—"; + const d = new Date(ts); + return isNaN(d.getTime()) ? ts : d.toLocaleString(); +}; + +function sortIds(ids: string[]) { + const allNum = ids.every((id) => /^\d+$/.test(id)); + return allNum + ? [...ids].sort((a, b) => Number(a) - Number(b)) + : [...ids].sort(); +} + +export function BenchmarkTimeSeriesComparisonTableSlider({ + items, + range, + onChange, +}: { + items: WorkflowItem[]; + range: [number, number]; // controlled index range + onChange: (next: [number, number]) => void; + labelForMark?: (w: WorkflowItem) => string; +}) { + // sort & map + const { ids, byId } = useMemo(() => { + const byId: Record = {}; + items.forEach((it) => (byId[it.workflow_id] = it)); + const ids = sortIds(items.map((it) => it.workflow_id)); + return { ids, byId }; + }, [items]); + + const lWorkflowId = ids[range[0]] ?? null; + const rWorkflowId = ids[range[1]] ?? null; + + // render slider tick labels when slider is hovered + function valueLabelFormat(idx: number) { + const wf = byId[ids[idx as number]]; + if (!wf) return ""; + + return ( + + WorkflowId: {wf.workflow_id} +
Commit: {shortSha(wf.commit)}
+
{fmtTs(wf.ts)}
+
+ ); + } + + function rangeLabelFormat(workflowId: string | number | null) { + if (!workflowId) return "-"; + const id = shortSha(workflowId as string); + const commit = byId[workflowId].commit + ? shortSha(byId[workflowId].commit) + : ""; + return `${id} (commit: ${commit})`; + } + + const handleChange = React.useCallback( + (_event: Event, value: number | number[], _activeThumb: number) => { + if (Array.isArray(value) && value.length === 2) { + const [a, b] = value[0] <= value[1] ? value : [value[1], value[0]]; + onChange([a, b]); + } + }, + [onChange] + ); + + const minWidth = Math.max(200, 50 * ids.length); + return ( + + + Select L / R Data + + + + + valueLabelFormat(idx)} + disableSwap + /> + + + + + {ids.length} Items + + + ); +} diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/helper.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/helper.tsx index 7932e9725e..399a31601a 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/helper.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/helper.tsx @@ -5,6 +5,21 @@ export type BenchmarkChartSectionConfig = { chartGroup: ChartGroupConfig; }; +export type BenchmarkComparisonTableSectionConfig = { + titleMapping?: Record; + groupByFields: string[]; + filterByFieldValues?: Record>; + tableConfig: ComparisonTableConfig; +}; + +export type ComparisonTableConfig = { + titleMapping?: Record; + nameKey: string; + renderOptions?: { + columnPolicy: any; + }; +}; + export type ChartGroupConfig = { type: "line"; titleMapping?: Record; diff --git a/torchci/components/benchmark/v3/components/dataRender/fanout/FanoutBenchmarkTimeSeriesChartSection.tsx b/torchci/components/benchmark/v3/components/dataRender/fanout/FanoutBenchmarkTimeSeriesChartSection.tsx deleted file mode 100644 index c3aeb7b272..0000000000 --- a/torchci/components/benchmark/v3/components/dataRender/fanout/FanoutBenchmarkTimeSeriesChartSection.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import BenchmarkChartSection from "../components/benchmarkTimeSeries/components/BenchmarkChartSection"; -import { - BenchmarkChartSectionConfig, - BenchmarkTimeSeriesInput, -} from "../components/benchmarkTimeSeries/helper"; - -export default function FanoutBenchmarkTimeSeriesChartSection({ - data = [], - config, - onChange, -}: { - data?: BenchmarkTimeSeriesInput[]; - config: any; - onChange?: (payload: any) => void; -}) { - return ( -
- { - onChange?.(payload); - }} - /> -
- ); -} diff --git a/torchci/components/benchmark/v3/components/dataRender/fanout/FanoutComponents.tsx b/torchci/components/benchmark/v3/components/dataRender/fanout/FanoutComponents.tsx new file mode 100644 index 0000000000..ad5a914972 --- /dev/null +++ b/torchci/components/benchmark/v3/components/dataRender/fanout/FanoutComponents.tsx @@ -0,0 +1,47 @@ +import BenchmarkChartSection from "../components/benchmarkTimeSeries/BenchmarkChartSection"; +import BenchmarkTimeSeriesComparisonTableSection from "../components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection"; +import { + BenchmarkChartSectionConfig, + BenchmarkTimeSeriesInput, +} from "../components/benchmarkTimeSeries/helper"; + +export function FanoutBenchmarkTimeSeriesChartSection({ + data = [], + config, + onChange, +}: { + data?: BenchmarkTimeSeriesInput[]; + config: any; + onChange?: (payload: any) => void; +}) { + return ( +
+ { + onChange?.(payload); + }} + /> +
+ ); +} + +export function FanoutBenchmarkTimeSeriesComparisonTableSection({ + data = [], + config, + onChange, +}: { + data?: any[]; + config: any; + onChange?: (payload: any) => void; +}) { + return ( + <> + + + ); +} diff --git a/torchci/components/benchmark/v3/configs/teams/compilers/config.ts b/torchci/components/benchmark/v3/configs/teams/compilers/config.ts index 64a11e28f2..9115215d16 100644 --- a/torchci/components/benchmark/v3/configs/teams/compilers/config.ts +++ b/torchci/components/benchmark/v3/configs/teams/compilers/config.ts @@ -99,6 +99,15 @@ export const CompilerPrecomputeBenchmarkUIConfig: BenchmarkUIConfig = { }, }, }, + { + type: "FanoutBenchmarkTimeSeriesComparisonTableSection", + config: { + groupByFields: ["metric"], + tableConfig: { + nameKey: "compiler", + }, + }, + }, ], }, }; diff --git a/torchci/components/benchmark/v3/configs/utils/fanoutRegistration.tsx b/torchci/components/benchmark/v3/configs/utils/fanoutRegistration.tsx index 590f2dcd9c..b8bd3528b4 100644 --- a/torchci/components/benchmark/v3/configs/utils/fanoutRegistration.tsx +++ b/torchci/components/benchmark/v3/configs/utils/fanoutRegistration.tsx @@ -1,4 +1,7 @@ -import FanoutBenchmarkTimeSeriesChartSection from "../../components/dataRender/fanout/FanoutBenchmarkTimeSeriesChartSection"; +import { + FanoutBenchmarkTimeSeriesChartSection, + FanoutBenchmarkTimeSeriesComparisonTableSection, +} from "../../components/dataRender/fanout/FanoutComponents"; /** ---------------- Types ---------------- */ export type FanoutComponentProps = { @@ -46,8 +49,11 @@ export class FanoutRegistry { Component: FanoutBenchmarkTimeSeriesChartSection, data_path: "time_series", }, + FanoutBenchmarkTimeSeriesComparisonTableSection: { + Component: FanoutBenchmarkTimeSeriesComparisonTableSection, + data_path: "table", + }, }; - this.map = Object.freeze({ ...registry }); this.fallback = Object.freeze({ Component: ErrorFanoutComponent }); Object.freeze(this); // freeze the instance so it can't be mutated diff --git a/torchci/lib/benchmark/api_helper/compilers/helpers/general.ts b/torchci/lib/benchmark/api_helper/compilers/helpers/general.ts index d0ed39d2d9..66e9dead48 100644 --- a/torchci/lib/benchmark/api_helper/compilers/helpers/general.ts +++ b/torchci/lib/benchmark/api_helper/compilers/helpers/general.ts @@ -26,6 +26,7 @@ const COMPILER_GENERAL_TABLE_GROUP_KEY = [ "branch", "compiler", "model", + "suite", ]; const COMPILER_GENERAL_TABLE_SUB_GROUP_KEY = ["metric"]; diff --git a/torchci/lib/benchmark/api_helper/compilers/helpers/precompute.ts b/torchci/lib/benchmark/api_helper/compilers/helpers/precompute.ts index be3ec4c6bf..a592506b66 100644 --- a/torchci/lib/benchmark/api_helper/compilers/helpers/precompute.ts +++ b/torchci/lib/benchmark/api_helper/compilers/helpers/precompute.ts @@ -33,11 +33,13 @@ const COMPILER_PRECOMPUTE_TABLE_GROUP_KEY = [ "arch", "device", "mode", - "metric", "workflow_id", + "commit", "branch", + "metric", + "compiler", ]; -const COMPILER_PRECOMPUTE_TABLE_SUB_GROUP_KEY = ["compiler"]; +const COMPILER_PRECOMPUTE_TABLE_SUB_GROUP_KEY = ["suite"]; export function toPrecomputeCompilerData( rawData: any[], From 674653eb6e0a70204bf8dcd81330dd7f0c08e299 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Wed, 17 Sep 2025 21:01:35 -0400 Subject: [PATCH 02/13] add config --- .../ComparisonTable.tsx | 8 +- .../ComparisonTableColumnRendering.tsx | 67 +++++-- .../ComparisonTableHelpers.tsx | 1 + .../components/benchmarkTimeSeries/helper.tsx | 9 +- .../v3/configs/helpers/RegressionPolicy.ts | 176 ++++++++++++++++++ .../v3/configs/teams/compilers/config.ts | 53 ++++++ .../benchmark/v3/configs/typeProcess.tsx | 0 7 files changed, 297 insertions(+), 17 deletions(-) create mode 100644 torchci/components/benchmark/v3/configs/helpers/RegressionPolicy.ts delete mode 100644 torchci/components/benchmark/v3/configs/typeProcess.tsx diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTable.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTable.tsx index 76bcd21b8e..a70452e93e 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTable.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTable.tsx @@ -27,6 +27,7 @@ export function ComparisonTable({ return getComparisonTableRowDefinition(config, data); }, [data, config.nameKey]); + console.log("Table Config", config); // union of all column ids const allColumns = useMemo(() => { const s = new Set(); @@ -45,7 +46,12 @@ export function ComparisonTable({ // Form the columns const columns: GridColDef[] = useMemo( () => - getComparisionTableConlumnRendering(allColumns, lWorkflowId, rWorkflowId), + getComparisionTableConlumnRendering( + allColumns, + lWorkflowId, + rWorkflowId, + config + ), [allColumns, lWorkflowId, rWorkflowId, title] ); diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableColumnRendering.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableColumnRendering.tsx index cf29318d23..97950cf41a 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableColumnRendering.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableColumnRendering.tsx @@ -6,6 +6,11 @@ import { GridRenderCellParams, GridRowModel, } from "@mui/x-data-grid"; +import { + BenchmarkComparisonPolicyConfig, + evaluateComparison, +} from "components/benchmark/v3/configs/helpers/RegressionPolicy"; +import { ComparisonTableConfig } from "../../../helper"; import { asNumber, valOf } from "./ComparisonTableHelpers"; /** @@ -18,7 +23,8 @@ import { asNumber, valOf } from "./ComparisonTableHelpers"; export function getComparisionTableConlumnRendering( columnsFields: string[], lWorkflowId: string | null, - rWorkflowId: string | null + rWorkflowId: string | null, + config: ComparisonTableConfig ): GridColDef[] { const nameCol: GridColDef = { field: "name", @@ -40,6 +46,7 @@ export function getComparisionTableConlumnRendering( row={params.row} lWorkflowId={lWorkflowId} rWorkflowId={rWorkflowId} + config={config} /> ), })); @@ -73,11 +80,14 @@ export function ComparisonTableValueCell({ row, lWorkflowId, rWorkflowId, + config, }: { field: string; row: GridRowModel; lWorkflowId: string | null; rWorkflowId: string | null; + comparisonTargetField?: string; + config?: ComparisonTableConfig; }) { // If your value is directly rows[col], drop `.data?.[0]` const L = valOf( @@ -94,18 +104,45 @@ export function ComparisonTableValueCell({ ); const fmt = (v: any) => - v == null ? "—" : typeof v === "number" ? Number(v).toFixed(2) : String(v); + v == null + ? "—" + : typeof v === "number" + ? Number(v).toFixed(2) + : String(v.toFixed(2)); + const ln = asNumber(L); const rn = asNumber(R); - const d = ln != null && rn != null ? rn - ln : null; - const dStr = d == null ? "—" : `${d >= 0 ? "+" : ""}${Number(d.toFixed(3))}`; - const bg = - d == null || d === 0 - ? undefined - : d > 0 - ? IMPROVEMENT_COLOR - : VIOLATE_RULE_COLOR; + // get comparison policy for the field + const targetPolicyField = config?.comparisonPolicyTargetField; + let comparisonPolicy: BenchmarkComparisonPolicyConfig | undefined = undefined; + if (targetPolicyField && config?.comparisonPolicy) { + const fieldValue = row[targetPolicyField]; + comparisonPolicy = fieldValue + ? config?.comparisonPolicy[fieldValue] + : undefined; + } + + // evaluate comparison + const result = evaluateComparison( + comparisonPolicy?.target, + ln, + rn, + comparisonPolicy + ); + + let bgColor = ""; + switch (result.verdict) { + case "good": + bgColor = IMPROVEMENT_COLOR; + break; + case "regression": + bgColor = VIOLATE_RULE_COLOR; + break; + case "neutral": + default: + break; + } const text = L == null && R == null @@ -114,13 +151,15 @@ export function ComparisonTableValueCell({ ? `N/A→${fmt(R)}` : R == null ? `${fmt(L)}→N/A` - : L === R + : fmt(L) === fmt(R) ? `${fmt(L)}` - : `${fmt(L)}→${fmt(R)} (${dStr})`; + : `${fmt(L)}→${fmt(R)} (${result.delta?.toFixed(2) ?? "N/A"})`; return ( - - {text} + + + {text} + ); } diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableHelpers.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableHelpers.tsx index ef2814ad96..9ee5de1966 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableHelpers.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableHelpers.tsx @@ -45,6 +45,7 @@ export function getComparisonTableRowDefinition( const name = config.nameKey ? gi?.[config.nameKey] : label; if (!m.has(key)) { m.set(key, { + ...gi, id: key, label, metric, diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/helper.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/helper.tsx index 399a31601a..7ae66f94e5 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/helper.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/helper.tsx @@ -1,3 +1,5 @@ +import { BenchmarkComparisonPolicyConfig } from "components/benchmark/v3/configs/helpers/RegressionPolicy"; + export type BenchmarkChartSectionConfig = { titleMapping?: Record; groupByFields: string[]; @@ -15,8 +17,11 @@ export type BenchmarkComparisonTableSectionConfig = { export type ComparisonTableConfig = { titleMapping?: Record; nameKey: string; - renderOptions?: { - columnPolicy: any; + renderOptions?: {}; + // indicates the field to use for comparison policy map + comparisonPolicyTargetField?: string; + comparisonPolicy?: { + [key: string]: BenchmarkComparisonPolicyConfig; }; }; diff --git a/torchci/components/benchmark/v3/configs/helpers/RegressionPolicy.ts b/torchci/components/benchmark/v3/configs/helpers/RegressionPolicy.ts new file mode 100644 index 0000000000..af711238c8 --- /dev/null +++ b/torchci/components/benchmark/v3/configs/helpers/RegressionPolicy.ts @@ -0,0 +1,176 @@ +export type ComparisonVerdict = "good" | "neutral" | "regression"; +export type ComparisonPolicyType = "ratio"; +export const DEFAULT_TYPE = "ratio"; +export const DEFAULT_BAD_RATIO = 0.9; +export const DEFAULT_GOOD_RATIO = 1.1; +export const DEFAULT_DIRECTION = "up"; + +export function getDefaultComparisonPolicy( + target: string +): BenchmarkComparisonPolicyConfig { + return { + target, + type: DEFAULT_TYPE, + ratioPolicy: { + badRatio: DEFAULT_BAD_RATIO, + goodRatio: DEFAULT_GOOD_RATIO, + direction: DEFAULT_DIRECTION, + }, + }; +} + +export type BenchmarkComparisonPolicyConfig = { + /** metric/column name this policy applies to */ + target: string; + + type?: ComparisonPolicyType; + + /** ratio-based thresholds relative to oldValue */ + ratioPolicy?: { + /** + * Optional threshold for "good" (clear improvement). + * + * Interpretation depends on `direction`: + * - direction = "up" (higher is better): + * newValue >= oldValue * goodRatio → verdict = "good" + * + * - direction = "down" (lower is better): + * newValue <= oldValue * goodRatio → verdict = "good" + * + * Example: + * direction = "up", goodRatio = 1.0 + * → new must be >= old (no drop) to be considered good. + * + * direction = "down", goodRatio = 0.95 + * → new must be <= 95% of old (≥5% faster) to be considered good. + */ + goodRatio?: number; + + /** + * Mandatory threshold for "regression". + * + * Interpretation depends on `direction`: + * - direction = "up" (higher is better): + * newValue <= oldValue * badRatio → verdict = "regression" + * + * - direction = "down" (lower is better): + * newValue >= oldValue * badRatio → verdict = "regression" + * + * Example: + * direction = "up", badRatio = 0.98 + * → new <= 98% of old (≥2% drop) is a regression. + * + * direction = "down", badRatio = 1.10 + * → new >= 110% of old (≥10% slower) is a regression. + */ + badRatio: number; + + /** + * Direction of improvement: + * - "up" → higher newValue is better (typical for accuracy, pass rate, throughput). + * - "down" → lower newValue is better (typical for latency, memory, runtime). + * + * Default: "up". + */ + direction?: "up" | "down"; + }; +}; + +export type ComparisonResult = { + target: string; + oldValue: number | null; + newValue: number | null; + ratio: number | null; + delta: number | null; + verdict: ComparisonVerdict; + reason?: string; + isDefaultPolicy: boolean; +}; + +// ------------------------------------------------------------------ +// Evaluator +// ------------------------------------------------------------------ + +export function evaluateComparison( + target: string | undefined | null, + oldValue: number | null | undefined, + newValue: number | null | undefined, + policy?: BenchmarkComparisonPolicyConfig +): ComparisonResult { + if (!policy || policy.type == null) { + policy = getDefaultComparisonPolicy("general"); + } + const type: ComparisonPolicyType = policy.type ?? "ratio"; + const base: ComparisonResult = { + target: target ?? "general", + ratio: null, + oldValue: oldValue ?? null, + newValue: newValue ?? null, + delta: oldValue != null && newValue != null ? newValue - oldValue : null, + verdict: "neutral", + isDefaultPolicy: !policy || policy.type == null, + }; + // missing values → neutral + if ( + oldValue == null || + newValue == null || + Number.isNaN(oldValue) || + Number.isNaN(newValue) + ) { + return { ...base, reason: "missing value" }; + } + switch (type) { + case "ratio": { + const rp = policy.ratioPolicy ?? { + badRatio: 0.9, + goodRatio: 1.1, + direction: "up", + }; + const dir = rp.direction ?? "up"; + // Compare with oldValue * ratio + if (dir === "up") { + if (rp.goodRatio != null && newValue > oldValue * rp.goodRatio) { + return { + ...base, + verdict: "good", + reason: `new > old * goodRatio (${rp.goodRatio})`, + }; + } + if (newValue < oldValue * rp.badRatio) { + return { + ...base, + verdict: "regression", + reason: `new ≤ old * badRatio (${rp.badRatio})`, + }; + } + return { + ...base, + verdict: "neutral", + reason: "between good/bad ratios", + }; + } else { + if (rp.goodRatio != null && newValue < oldValue * rp.goodRatio) { + return { + ...base, + verdict: "good", + reason: `new ≤ old * goodRatio (${rp.goodRatio})`, + }; + } + if (newValue > oldValue * rp.badRatio) { + return { + ...base, + verdict: "regression", + reason: `new ≥ old * badRatio (${rp.badRatio})`, + }; + } + return { + ...base, + verdict: "neutral", + reason: "between good/bad ratios", + }; + } + } + default: + return { ...base, verdict: "neutral", reason: "no policy" }; + } +} diff --git a/torchci/components/benchmark/v3/configs/teams/compilers/config.ts b/torchci/components/benchmark/v3/configs/teams/compilers/config.ts index 9115215d16..ca27454b26 100644 --- a/torchci/components/benchmark/v3/configs/teams/compilers/config.ts +++ b/torchci/components/benchmark/v3/configs/teams/compilers/config.ts @@ -9,12 +9,50 @@ import dayjs from "dayjs"; import utc from "dayjs/plugin/utc"; import { REQUIRED_COMPLIER_LIST_COMMITS_KEYS } from "lib/benchmark/api_helper/compilers/type"; import { BenchmarkUIConfig } from "../../configBook"; +import { BenchmarkComparisonPolicyConfig } from "../../helpers/RegressionPolicy"; import { QueryParameterConverter, QueryParameterConverterInputs, } from "../../utils/dataBindingRegistration"; dayjs.extend(utc); +const PASSRATE_COMPARISON_POLICY: BenchmarkComparisonPolicyConfig = { + target: "passrate", + type: "ratio", + ratioPolicy: { + badRatio: 0.95, + goodRatio: 1.05, + direction: "up", + }, +}; +const GEOMEAN_COMPARISON_POLICY: BenchmarkComparisonPolicyConfig = { + target: "geomean", + type: "ratio", + ratioPolicy: { + badRatio: 0.95, + goodRatio: 1.05, + direction: "up", + }, +}; +const EXECUTION_TIME_COMPARISON_POLICY: BenchmarkComparisonPolicyConfig = { + target: "execution_time", + type: "ratio", + ratioPolicy: { + badRatio: 1.1, + goodRatio: 0.9, + direction: "down", + }, +}; +const COMPILATION_LATENCY_POLICY: BenchmarkComparisonPolicyConfig = { + target: "compilation_latency", + type: "ratio", + ratioPolicy: { + badRatio: 0.95, + goodRatio: 1.05, + direction: "up", + }, +}; + export const compilerQueryParameterConverter: QueryParameterConverter = ( inputs: QueryParameterConverterInputs ) => { @@ -103,8 +141,23 @@ export const CompilerPrecomputeBenchmarkUIConfig: BenchmarkUIConfig = { type: "FanoutBenchmarkTimeSeriesComparisonTableSection", config: { groupByFields: ["metric"], + filterByFieldValues: { + metric: [ + "passrate", + "geomean", + "execution_time", + "compilation_latency", + ], + }, tableConfig: { nameKey: "compiler", + comparisonPolicyTargetField: "metric", + comparisonPolicy: { + passrate: PASSRATE_COMPARISON_POLICY, + geomean: GEOMEAN_COMPARISON_POLICY, + execution_time: EXECUTION_TIME_COMPARISON_POLICY, + compilation_latency: COMPILATION_LATENCY_POLICY, + }, }, }, }, diff --git a/torchci/components/benchmark/v3/configs/typeProcess.tsx b/torchci/components/benchmark/v3/configs/typeProcess.tsx deleted file mode 100644 index e69de29bb2..0000000000 From 9ebd8fe04a515c03a0609b9f334a9b8bbec82faf Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Wed, 17 Sep 2025 21:04:28 -0400 Subject: [PATCH 03/13] add config --- .../BenchmarkTimeSeriesComparisonTableSection.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx index ed81e20540..26001989e5 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx @@ -60,14 +60,10 @@ export default function BenchmarkTimeSeriesComparisonTableSection({ return m; }, [filtered, tableSectionConfig.groupByFields]); - if (!data || data.length == 0) { - return <>; - } - // Build a list of workflows for the slider (you can add labels/timestamps here) const items: any[] = useMemo(() => { const idMap = new Map(); - for (const d of data) { + for (const d of filtered) { const id = String(d.group_info.workflow_id); const commit = String(d.group_info.commit); idMap.set(id, { @@ -84,6 +80,10 @@ export default function BenchmarkTimeSeriesComparisonTableSection({ tableSectionConfig.filterByFieldValues, ]); + if (!data || data.length == 0) { + return <>; + } + // Controlled slider range (indices) const [range, setRange] = useState<[number, number]>(() => { const n = items.length; From c25bbd1ebd82de6760ba16ed0723f74ff8cf53d4 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Wed, 17 Sep 2025 21:09:58 -0400 Subject: [PATCH 04/13] add config --- .../BenchmarkTimeSeriesComparisonTableSection.tsx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx index 26001989e5..86c2d72f15 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx @@ -34,6 +34,11 @@ export default function BenchmarkTimeSeriesComparisonTableSection({ const [barOffset, setBarOffset] = useState(0); const handleMount = (h: number) => setBarOffset((prev) => prev + h); const handleUnmount = (h: number) => setBarOffset((prev) => prev - h); + // Controlled slider range (indices) + const [range, setRange] = useState<[number, number]>(() => { + const n = items.length; + return n >= 2 ? [0, n - 1] : [0, 0]; + }); // Filter data based on the table config const filtered = useMemo(() => { @@ -80,16 +85,10 @@ export default function BenchmarkTimeSeriesComparisonTableSection({ tableSectionConfig.filterByFieldValues, ]); - if (!data || data.length == 0) { + if (!data || data.length == 0) { return <>; } - // Controlled slider range (indices) - const [range, setRange] = useState<[number, number]>(() => { - const n = items.length; - return n >= 2 ? [0, n - 1] : [0, 0]; - }); - // Derive L/R workflow IDs from the range const sortedIds = useMemo( () => From 341345ff485667170ec99799107b91292cf847b5 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Wed, 17 Sep 2025 22:10:04 -0400 Subject: [PATCH 05/13] add config --- ...chmarkTimeSeriesComparisonTableSection.tsx | 97 +++++++------------ ...nchmarkTimeSeriesComparisonTableSlider.tsx | 77 +++++++++------ .../components/benchmarkTimeSeries/helper.tsx | 47 +++++++++ 3 files changed, 127 insertions(+), 94 deletions(-) diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx index 86c2d72f15..4034145b0a 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx @@ -4,8 +4,9 @@ import { StickyBar } from "components/benchmark/v3/components/common/StickyBar"; import { useMemo, useState } from "react"; import { BenchmarkComparisonTableSectionConfig, - makeGroupKeyAndLabel, passesFilter, + toGroupKeyMap, + toSortedWorkflowIdMap, } from "../../helper"; import { ComparisonTable } from "./BenchmarkTimeSeriesComparisonTable/ComparisonTable"; import { BenchmarkTimeSeriesComparisonTableSlider } from "./BenchmarkTimeSeriesComparisonTableSlider"; @@ -34,78 +35,49 @@ export default function BenchmarkTimeSeriesComparisonTableSection({ const [barOffset, setBarOffset] = useState(0); const handleMount = (h: number) => setBarOffset((prev) => prev + h); const handleUnmount = (h: number) => setBarOffset((prev) => prev - h); - // Controlled slider range (indices) - const [range, setRange] = useState<[number, number]>(() => { - const n = items.length; - return n >= 2 ? [0, n - 1] : [0, 0]; - }); // Filter data based on the table config const filtered = useMemo(() => { - if (!data) { - return []; - } + if (!data) return []; return data.filter((s) => passesFilter(s.group_info || {}, tableSectionConfig.filterByFieldValues) ); }, [data, tableSectionConfig.filterByFieldValues]); // Group data based on the table config - const groupMap = useMemo(() => { - const m = new Map(); - for (const s of filtered) { - const gi = s.group_info || {}; - const { key, labels } = makeGroupKeyAndLabel( - gi, - tableSectionConfig.groupByFields - ); - if (!m.has(key)) m.set(key, { key, labels, items: [] }); - m.get(key)!.items.push(s); - } - return m; - }, [filtered, tableSectionConfig.groupByFields]); - - // Build a list of workflows for the slider (you can add labels/timestamps here) - const items: any[] = useMemo(() => { - const idMap = new Map(); - for (const d of filtered) { - const id = String(d.group_info.workflow_id); - const commit = String(d.group_info.commit); - idMap.set(id, { - workflow_id: id, - label: id, - commit: commit, // keep previous commit if any - branch: d.group_info.branch, - }); - } - return Array.from(idMap.values()); - }, [ - filtered, - tableSectionConfig.groupByFields, - tableSectionConfig.filterByFieldValues, - ]); + const groupMap = useMemo( + () => toGroupKeyMap(filtered, tableSectionConfig.groupByFields), + [filtered, tableSectionConfig.groupByFields] + ); - if (!data || data.length == 0) { - return <>; - } + const workflowMetadataInfos: any[] = useMemo( + () => toSortedWorkflowIdMap(filtered), + [ + filtered, + tableSectionConfig.groupByFields, + tableSectionConfig.filterByFieldValues, + ] + ); - // Derive L/R workflow IDs from the range - const sortedIds = useMemo( - () => - items - .map((i) => i.workflow_id) - .sort((a, b) => { - const na = /^\d+$/.test(a) ? Number(a) : NaN; - const nb = /^\d+$/.test(b) ? Number(b) : NaN; - return Number.isNaN(na) || Number.isNaN(nb) - ? a.localeCompare(b) - : na - nb; - }), - [items] + const [lWorkflowId, setLlWorkflowId] = useState( + workflowMetadataInfos.length > 0 + ? workflowMetadataInfos[0].workflow_id + : null ); + const [rWorkflowId, setRWorkflowId] = useState( + workflowMetadataInfos.length > 0 + ? workflowMetadataInfos[workflowMetadataInfos.length - 1].workflow_id + : null + ); + + const onSliderChange = (next: [string, string]) => { + setLlWorkflowId(next[0]); + setRWorkflowId(next[1]); + }; - const lWorkflowId = sortedIds[range[0]] ?? null; - const rWorkflowId = sortedIds[range[1]] ?? null; + if (!filtered || filtered.length == 0) { + return <>; + } return ( @@ -121,9 +93,8 @@ export default function BenchmarkTimeSeriesComparisonTableSection({ onUnmount={handleUnmount} > diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSlider.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSlider.tsx index dc93321047..00f16ddb38 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSlider.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSlider.tsx @@ -1,11 +1,11 @@ import styled from "@emotion/styled"; import { Box, Chip, Paper, Slider, Stack, Typography } from "@mui/material"; -import * as React from "react"; -import { useMemo } from "react"; +import { useCallback, useMemo, useState } from "react"; -export type WorkflowItem = { +export type WorkflowMetaInfo = { workflow_id: string; commit: string; + branch: string; [k: string]: any; }; @@ -36,25 +36,49 @@ function sortIds(ids: string[]) { } export function BenchmarkTimeSeriesComparisonTableSlider({ - items, - range, + workflows, onChange, }: { - items: WorkflowItem[]; - range: [number, number]; // controlled index range - onChange: (next: [number, number]) => void; - labelForMark?: (w: WorkflowItem) => string; + workflows: WorkflowMetaInfo[]; + onChange: (next: [string, string]) => void; }) { // sort & map const { ids, byId } = useMemo(() => { - const byId: Record = {}; - items.forEach((it) => (byId[it.workflow_id] = it)); - const ids = sortIds(items.map((it) => it.workflow_id)); + const byId: Record = {}; + workflows.forEach((it) => (byId[it.workflow_id] = it)); + const ids = sortIds(workflows.map((it) => it.workflow_id)); return { ids, byId }; - }, [items]); + }, [workflows]); - const lWorkflowId = ids[range[0]] ?? null; - const rWorkflowId = ids[range[1]] ?? null; + // Controlled slider range (indices) + const [range, setRange] = useState<[number, number]>(() => { + const n = workflows.length; + return n >= 2 ? [0, n - 1] : [0, 0]; + }); + + const [lWorkflowId, rWorkflowId] = useMemo(() => { + const [a, b] = range; + return [ids[a], ids[b]]; + }, [range, ids]); + + // update range when workflows change + useMemo(() => { + const n = workflows.length; + if (n >= 2) { + setRange([0, n - 1]); + } else { + setRange([0, 0]); + } + }, [workflows]); + + function rangeLabelFormat(workflowId: string | number | null) { + if (!workflowId) return "-"; + const id = shortSha(workflowId as string); + const commit = byId[workflowId].commit + ? shortSha(byId[workflowId].commit) + : ""; + return `${id} (commit: ${commit})`; + } // render slider tick labels when slider is hovered function valueLabelFormat(idx: number) { @@ -69,21 +93,15 @@ export function BenchmarkTimeSeriesComparisonTableSlider({ ); } - - function rangeLabelFormat(workflowId: string | number | null) { - if (!workflowId) return "-"; - const id = shortSha(workflowId as string); - const commit = byId[workflowId].commit - ? shortSha(byId[workflowId].commit) - : ""; - return `${id} (commit: ${commit})`; - } - - const handleChange = React.useCallback( + + const handleChange = useCallback( (_event: Event, value: number | number[], _activeThumb: number) => { if (Array.isArray(value) && value.length === 2) { const [a, b] = value[0] <= value[1] ? value : [value[1], value[0]]; - onChange([a, b]); + setRange([a, b]); + const l = ids[a]; + const r = ids[b]; + onChange([l, r]); } }, [onChange] @@ -109,11 +127,8 @@ export function BenchmarkTimeSeriesComparisonTableSlider({ disableSwap /> - + - - {ids.length} Items - ); } diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/helper.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/helper.tsx index 7ae66f94e5..7cb879b281 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/helper.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/helper.tsx @@ -84,3 +84,50 @@ export function makeGroupKeyAndLabel( const labels = fields.map((f) => `${toStr(gi?.[f])}`); return { key: parts.join("|"), labels }; } + +export function toGroupKeyMap(data: any[], fields: string[]) { + const m = new Map(); + for (const s of data) { + const gi = s.group_info || {}; + const { key, labels } = makeGroupKeyAndLabel(gi, fields); + if (!m.has(key)) m.set(key, { key, labels, items: [] }); + m.get(key)!.items.push(s); + } + return m; +} + +/** + * + * @param data data list, the gruop_info in data object must have workflow_id, commit and branch + * @returns + */ +export function toSortedWorkflowIdMap(data: any[]) { + const workflowIdMap = new Map(); + for (const d of data) { + if (!d.group_info) { + throw new Error( + "[toSortedWorkflowIdMap]group_info is missing when try to form the workflowIdMap " + ); + } + if (!d.group_info.workflow_id) { + throw new Error( + "[toSortedWorkflowIdMap]workflow_id is missing when try to form the workflowIdMap " + ); + } + const id = String(d.group_info.workflow_id); + workflowIdMap.set(id, { + workflow_id: id, + label: id, + commit: d.group_info.commit, + branch: d.group_info.branch, + }); + } + // Sort by numeric if all ids are numbers, else lexicographically + return Array.from(workflowIdMap.values()).sort((a, b) => { + const na = /^\d+$/.test(a.workflow_id) ? Number(a.workflow_id) : NaN; + const nb = /^\d+$/.test(b.workflow_id) ? Number(b.workflow_id) : NaN; + return Number.isNaN(na) || Number.isNaN(nb) + ? a.workflow_id.localeCompare(b.workflow_id) + : na - nb; + }); +} From 550ff4776c9275506911adaa1666907661263ea5 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Wed, 17 Sep 2025 22:10:43 -0400 Subject: [PATCH 06/13] add config --- .../ComparisonTableColumnRendering.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableColumnRendering.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableColumnRendering.tsx index 97950cf41a..e02861125b 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableColumnRendering.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableColumnRendering.tsx @@ -89,7 +89,6 @@ export function ComparisonTableValueCell({ comparisonTargetField?: string; config?: ComparisonTableConfig; }) { - // If your value is directly rows[col], drop `.data?.[0]` const L = valOf( lWorkflowId ? row.byWorkflow[lWorkflowId]?.[field]?.data?.[0] ?? From 9ccc10af2a9f0d985e6e59111525d7e045286cbc Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Wed, 17 Sep 2025 22:13:10 -0400 Subject: [PATCH 07/13] add config --- .../BenchmarkTimeSeriesComparisonTableSection.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx index 4034145b0a..125be164b2 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx @@ -98,9 +98,9 @@ export default function BenchmarkTimeSeriesComparisonTableSection({ /> - {Array.from(groupMap.entries()).map(([key, data]) => { - if (!data) return null; - const title = data.labels.join(" "); + {Array.from(groupMap.entries()).map(([key, tableData]) => { + if (!tableData) return null; + const title = tableData.labels.join(" "); return ( @@ -109,7 +109,7 @@ export default function BenchmarkTimeSeriesComparisonTableSection({ {lWorkflowId} - {rWorkflowId} Date: Wed, 17 Sep 2025 22:33:40 -0400 Subject: [PATCH 08/13] add config --- .../ComparisonTable.tsx | 56 ++++++++++--------- ...chmarkTimeSeriesComparisonTableSection.tsx | 7 +-- ...nchmarkTimeSeriesComparisonTableSlider.tsx | 33 ++++------- 3 files changed, 43 insertions(+), 53 deletions(-) diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTable.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTable.tsx index a70452e93e..4d16546cad 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTable.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTable.tsx @@ -1,3 +1,4 @@ +import { Typography } from "@mui/material"; import { DataGrid, GridColDef, GridRowModel } from "@mui/x-data-grid"; import { useMemo } from "react"; import { ComparisonTableConfig } from "../../../helper"; @@ -27,7 +28,6 @@ export function ComparisonTable({ return getComparisonTableRowDefinition(config, data); }, [data, config.nameKey]); - console.log("Table Config", config); // union of all column ids const allColumns = useMemo(() => { const s = new Set(); @@ -56,29 +56,35 @@ export function ComparisonTable({ ); return ( - r.id} - sx={{ - "& .MuiDataGrid-cell": { - py: 0, // less vertical padding - fontSize: "0.75rem", - }, - "& .MuiDataGrid-columnHeaders": { - minHeight: 32, - maxHeight: 32, - lineHeight: "32px", - fontSize: "0.75rem", - }, - "& .MuiDataGrid-row": { - minHeight: 32, - maxHeight: 32, - }, - }} - hideFooter - /> + <> + {title.toUpperCase()} + + {lWorkflowId} - {rWorkflowId} + + r.id} + sx={{ + "& .MuiDataGrid-cell": { + py: 0, // less vertical padding + fontSize: "0.75rem", + }, + "& .MuiDataGrid-columnHeaders": { + minHeight: 32, + maxHeight: 32, + lineHeight: "32px", + fontSize: "0.75rem", + }, + "& .MuiDataGrid-row": { + minHeight: 32, + maxHeight: 32, + }, + }} + hideFooter + /> + ); } diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx index 125be164b2..2b1496fbaa 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx @@ -32,7 +32,7 @@ export default function BenchmarkTimeSeriesComparisonTableSection({ onChange?: (payload: any) => void; }) { // Sticky bar offset - const [barOffset, setBarOffset] = useState(0); + const [barOffset, setBarOffset] = useState(-20); const handleMount = (h: number) => setBarOffset((prev) => prev + h); const handleUnmount = (h: number) => setBarOffset((prev) => prev - h); @@ -104,15 +104,12 @@ export default function BenchmarkTimeSeriesComparisonTableSection({ return ( - {title.toUpperCase()} - - {lWorkflowId} - {rWorkflowId} - diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSlider.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSlider.tsx index 00f16ddb38..50fb7ee38a 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSlider.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSlider.tsx @@ -28,13 +28,6 @@ const fmtTs = (ts?: string) => { return isNaN(d.getTime()) ? ts : d.toLocaleString(); }; -function sortIds(ids: string[]) { - const allNum = ids.every((id) => /^\d+$/.test(id)); - return allNum - ? [...ids].sort((a, b) => Number(a) - Number(b)) - : [...ids].sort(); -} - export function BenchmarkTimeSeriesComparisonTableSlider({ workflows, onChange, @@ -46,7 +39,7 @@ export function BenchmarkTimeSeriesComparisonTableSlider({ const { ids, byId } = useMemo(() => { const byId: Record = {}; workflows.forEach((it) => (byId[it.workflow_id] = it)); - const ids = sortIds(workflows.map((it) => it.workflow_id)); + const ids = workflows.map((it) => it.workflow_id); return { ids, byId }; }, [workflows]); @@ -56,11 +49,6 @@ export function BenchmarkTimeSeriesComparisonTableSlider({ return n >= 2 ? [0, n - 1] : [0, 0]; }); - const [lWorkflowId, rWorkflowId] = useMemo(() => { - const [a, b] = range; - return [ids[a], ids[b]]; - }, [range, ids]); - // update range when workflows change useMemo(() => { const n = workflows.length; @@ -71,13 +59,11 @@ export function BenchmarkTimeSeriesComparisonTableSlider({ } }, [workflows]); - function rangeLabelFormat(workflowId: string | number | null) { - if (!workflowId) return "-"; - const id = shortSha(workflowId as string); - const commit = byId[workflowId].commit - ? shortSha(byId[workflowId].commit) - : ""; - return `${id} (commit: ${commit})`; + function rangeLabelFormat(wfi: any) { + const wf = byId[ids[wfi as number]]; + if (!wf) return "-"; + const commit = wf.commit ? shortSha(wf.commit) : ""; + return `${wf.workflow_id} (commit: ${commit})`; } // render slider tick labels when slider is hovered @@ -93,7 +79,7 @@ export function BenchmarkTimeSeriesComparisonTableSlider({
); } - + const handleChange = useCallback( (_event: Event, value: number | number[], _activeThumb: number) => { if (Array.isArray(value) && value.length === 2) { @@ -101,6 +87,7 @@ export function BenchmarkTimeSeriesComparisonTableSlider({ setRange([a, b]); const l = ids[a]; const r = ids[b]; + console.log("onChange", l, r); onChange([l, r]); } }, @@ -114,7 +101,7 @@ export function BenchmarkTimeSeriesComparisonTableSlider({ Select L / R Data - + - + ); From d51e89f28d7af7da7c0520d76922a7fd89ef7164 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Wed, 17 Sep 2025 22:41:46 -0400 Subject: [PATCH 09/13] add config --- .../BenchmarkTimeSeriesComparisonTableSection.tsx | 1 - .../BenchmarkTimeSeriesComparisonTableSlider.tsx | 4 +--- .../dataRender/components/benchmarkTimeSeries/helper.tsx | 3 +++ 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx index 2b1496fbaa..d7496fb592 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx @@ -85,7 +85,6 @@ export default function BenchmarkTimeSeriesComparisonTableSection({ ({ }, })); -const shortSha = (id?: string) => - id ? (id.length > 10 ? id.slice(0, 7) : id) : "—"; - const fmtTs = (ts?: string) => { if (!ts) return "—"; const d = new Date(ts); diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/helper.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/helper.tsx index 7cb879b281..fb2ba472ae 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/helper.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/helper.tsx @@ -131,3 +131,6 @@ export function toSortedWorkflowIdMap(data: any[]) { : na - nb; }); } + +export const shortSha = (id?: string) => + id ? (id.length > 10 ? id.slice(0, 7) : id) : "—"; From 5dc7fb53288b7291c7ed4347de63efb67a6e86bf Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Wed, 17 Sep 2025 23:01:37 -0400 Subject: [PATCH 10/13] add config --- .../benchmark/v3/components/common/StickyBar.tsx | 15 +++++++++++---- .../benchmarkTimeSeries/BenchmarkChartSection.tsx | 4 ++-- .../ComparisonTable.tsx | 2 +- .../BenchmarkTimeSeriesComparisonTableSection.tsx | 2 +- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/torchci/components/benchmark/v3/components/common/StickyBar.tsx b/torchci/components/benchmark/v3/components/common/StickyBar.tsx index 3af46ab238..71f36ab036 100644 --- a/torchci/components/benchmark/v3/components/common/StickyBar.tsx +++ b/torchci/components/benchmark/v3/components/common/StickyBar.tsx @@ -33,11 +33,18 @@ export const StickyBar: React.FC = ({ return () => onUnmount?.(height); }, [height, onMount, onUnmount]); - // IntersectionObserver: sticky only after scroll useEffect(() => { - const observer = new IntersectionObserver(([entry]) => { - setIsSticky(!entry.isIntersecting); - }); + const observer = new IntersectionObserver( + ([entry]) => { + // < 0.99 + setIsSticky(entry.intersectionRatio < 0.99); + }, + { + threshold: Array.from({ length: 101 }, (_, i) => i / 100), + // 0,0.01,0.02,...,1, + } + ); + if (ref.current) observer.observe(ref.current); return () => observer.disconnect(); }, []); diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/BenchmarkChartSection.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/BenchmarkChartSection.tsx index f52ce6848d..992dad73c6 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/BenchmarkChartSection.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/BenchmarkChartSection.tsx @@ -66,7 +66,7 @@ export default function BenchmarkChartSection({ return ( - Time Series Chart Section + Time Series Chart Section {Array.from(groupMap.entries()).map(([key, data]) => { if (!data) return null; @@ -83,7 +83,7 @@ export default function BenchmarkChartSection({ return ( - {title.toUpperCase()} + {title.toUpperCase()} - {title.toUpperCase()} + {title.toUpperCase()} {lWorkflowId} - {rWorkflowId} diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx index d7496fb592..96c655bf48 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx @@ -81,7 +81,7 @@ export default function BenchmarkTimeSeriesComparisonTableSection({ return ( - Time Series Comparison Section + Time Series Comparison Section Date: Thu, 18 Sep 2025 01:10:46 -0400 Subject: [PATCH 11/13] add config --- .../ComparisonTable.tsx | 11 +++---- .../ComparisonTableHelpers.tsx | 30 ++++++++++--------- ...chmarkTimeSeriesComparisonTableSection.tsx | 12 ++++---- .../components/benchmarkTimeSeries/helper.tsx | 2 +- .../benchmark/v3/configs/configBook.tsx | 1 + .../v3/configs/teams/compilers/config.ts | 2 +- .../api_helper/compilers/helpers/general.ts | 4 +-- .../compilers/helpers/precompute.ts | 4 +-- torchci/lib/benchmark/api_helper/utils.ts | 19 ++++++++++-- 9 files changed, 49 insertions(+), 36 deletions(-) diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTable.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTable.tsx index e126d39eea..f3b2cb51e5 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTable.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTable.tsx @@ -3,10 +3,7 @@ import { DataGrid, GridColDef, GridRowModel } from "@mui/x-data-grid"; import { useMemo } from "react"; import { ComparisonTableConfig } from "../../../helper"; import { getComparisionTableConlumnRendering } from "./ComparisonTableColumnRendering"; -import { - getComparisonTableRowDefinition, - SnapshotRow, -} from "./ComparisonTableHelpers"; +import { SnapshotRow, ToComparisonTableRow } from "./ComparisonTableHelpers"; export function ComparisonTable({ data, @@ -25,10 +22,10 @@ export function ComparisonTable({ }) { // group raw data into rows, each row contains all values across workflowIds const rows: GridRowModel[] = useMemo(() => { - return getComparisonTableRowDefinition(config, data); - }, [data, config.nameKey]); + return ToComparisonTableRow(config, data); + }, [data]); - // union of all column ids + // iterate the column map in row data, and get all column names const allColumns = useMemo(() => { const s = new Set(); rows.forEach((r) => diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableHelpers.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableHelpers.tsx index 9ee5de1966..fa7c004e1b 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableHelpers.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableHelpers.tsx @@ -5,7 +5,7 @@ type GridRowModel = { label: string; name: string; metric: string; - byWorkflow: Record; + byWorkflow: Record; sampleInfo: any; }; @@ -20,41 +20,43 @@ function getGroupKeyAndLabel(gi: any) { return { key, label, metric: String(gi?.metric ?? "") }; } -/** Input types (your shape) */ export type RowCellObj = { value: number | string | null | undefined; [k: string]: any; }; -export type RowColumns = Record; -export type SnapshotRow = { group_info: any; rows: RowColumns }; +export type SnapshotRow = { + group_info: any; + sub_keys: string[]; + group_keys: string[]; + rows: RowCellObj[]; +}; /** Helpers */ export const asNumber = (v: unknown) => (typeof v === "number" ? v : undefined); export const valOf = (cell?: RowCellObj) => (cell ? cell.value : undefined); -export function getComparisonTableRowDefinition( - config: ComparisonTableConfig, - data: any -) { +export function ToComparisonTableRow(config: ComparisonTableConfig, data: any) { const m = new Map(); - for (const item of data ?? []) { - const gi = item.group_info ?? {}; + for (const rowData of data ?? []) { + const gi = rowData.group_info ?? {}; const wf = String(gi?.workflow_id ?? ""); - const { key, label, metric } = getGroupKeyAndLabel(gi); + const { key, label } = getGroupKeyAndLabel(gi); - const name = config.nameKey ? gi?.[config.nameKey] : label; + const name = config?.nameKeys + ? config.nameKeys.map((k) => gi[k]).join(" · ") + : label; + const rowDataMap = rowData.data ?? {}; if (!m.has(key)) { m.set(key, { ...gi, id: key, label, - metric, byWorkflow: {}, sampleInfo: gi, name, }); } - m.get(key)!.byWorkflow[wf] = item.rows ?? {}; + m.get(key)!.byWorkflow[wf] = rowDataMap; } return Array.from(m.values()); } diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx index 96c655bf48..49e63ab9a2 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTableSection.tsx @@ -50,7 +50,7 @@ export default function BenchmarkTimeSeriesComparisonTableSection({ [filtered, tableSectionConfig.groupByFields] ); - const workflowMetadataInfos: any[] = useMemo( + const workflowInfos: any[] = useMemo( () => toSortedWorkflowIdMap(filtered), [ filtered, @@ -60,13 +60,11 @@ export default function BenchmarkTimeSeriesComparisonTableSection({ ); const [lWorkflowId, setLlWorkflowId] = useState( - workflowMetadataInfos.length > 0 - ? workflowMetadataInfos[0].workflow_id - : null + workflowInfos.length > 0 ? workflowInfos[0].workflow_id : null ); const [rWorkflowId, setRWorkflowId] = useState( - workflowMetadataInfos.length > 0 - ? workflowMetadataInfos[workflowMetadataInfos.length - 1].workflow_id + workflowInfos.length > 0 + ? workflowInfos[workflowInfos.length - 1].workflow_id : null ); @@ -92,7 +90,7 @@ export default function BenchmarkTimeSeriesComparisonTableSection({ onUnmount={handleUnmount} > diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/helper.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/helper.tsx index fb2ba472ae..62d1060409 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/helper.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/helper.tsx @@ -16,7 +16,7 @@ export type BenchmarkComparisonTableSectionConfig = { export type ComparisonTableConfig = { titleMapping?: Record; - nameKey: string; + nameKeys?: string[]; // the field name used to render the name of the row, if not set, use all groupinfo labels renderOptions?: {}; // indicates the field to use for comparison policy map comparisonPolicyTargetField?: string; diff --git a/torchci/components/benchmark/v3/configs/configBook.tsx b/torchci/components/benchmark/v3/configs/configBook.tsx index 2b023dd4c7..1ae8dedec7 100644 --- a/torchci/components/benchmark/v3/configs/configBook.tsx +++ b/torchci/components/benchmark/v3/configs/configBook.tsx @@ -16,6 +16,7 @@ export type UIRenderConfig = { export type DataRenderOption = { type: string; + api?: any; id?: string; // id of the component to render, this is used when type is 'component' renders?: UIRenderConfig[]; // this is used when type is predefined type such as 'default-fanout' }; diff --git a/torchci/components/benchmark/v3/configs/teams/compilers/config.ts b/torchci/components/benchmark/v3/configs/teams/compilers/config.ts index ca27454b26..dd3eba562e 100644 --- a/torchci/components/benchmark/v3/configs/teams/compilers/config.ts +++ b/torchci/components/benchmark/v3/configs/teams/compilers/config.ts @@ -150,7 +150,7 @@ export const CompilerPrecomputeBenchmarkUIConfig: BenchmarkUIConfig = { ], }, tableConfig: { - nameKey: "compiler", + nameKeys: ["compiler"], comparisonPolicyTargetField: "metric", comparisonPolicy: { passrate: PASSRATE_COMPARISON_POLICY, diff --git a/torchci/lib/benchmark/api_helper/compilers/helpers/general.ts b/torchci/lib/benchmark/api_helper/compilers/helpers/general.ts index 66e9dead48..ad0a49b6a5 100644 --- a/torchci/lib/benchmark/api_helper/compilers/helpers/general.ts +++ b/torchci/lib/benchmark/api_helper/compilers/helpers/general.ts @@ -1,5 +1,5 @@ import { - groupByBenchmarkData, + to_table, to_time_series_data, toTimeSeriesResponse, } from "../../utils"; @@ -63,7 +63,7 @@ function getformat(data: any, format: string) { COMPILER_GENERAL_TS_SUB_GROUP_KEY ); case "table": - return groupByBenchmarkData( + return to_table( data, COMPILER_GENERAL_TABLE_GROUP_KEY, COMPILER_GENERAL_TABLE_SUB_GROUP_KEY diff --git a/torchci/lib/benchmark/api_helper/compilers/helpers/precompute.ts b/torchci/lib/benchmark/api_helper/compilers/helpers/precompute.ts index a592506b66..1167a68967 100644 --- a/torchci/lib/benchmark/api_helper/compilers/helpers/precompute.ts +++ b/torchci/lib/benchmark/api_helper/compilers/helpers/precompute.ts @@ -9,7 +9,7 @@ import { getPassingModels, } from "lib/benchmark/compilerUtils"; import { - groupByBenchmarkData, + to_table, to_time_series_data, toTimeSeriesResponse, toWorkflowIdMap, @@ -123,7 +123,7 @@ function getFormat(data: any, format: string) { ); break; case "table": - return groupByBenchmarkData( + return to_table( data, COMPILER_PRECOMPUTE_TABLE_GROUP_KEY, COMPILER_PRECOMPUTE_TABLE_SUB_GROUP_KEY diff --git a/torchci/lib/benchmark/api_helper/utils.ts b/torchci/lib/benchmark/api_helper/utils.ts index 11d05b4a7a..fbd744b8cf 100644 --- a/torchci/lib/benchmark/api_helper/utils.ts +++ b/torchci/lib/benchmark/api_helper/utils.ts @@ -258,7 +258,6 @@ export function to_time_series_data( if (item.data.length > 1) { const key = makeGroupKey(group_info); const sub_key = makeGroupKey(item.group_info); - diffs.push({ key: `${key}___${sub_key}`, data: item.data, @@ -271,7 +270,6 @@ export function to_time_series_data( new Date(a.granularity_bucket).getTime() - new Date(b.granularity_bucket).getTime() ); - if (diffs.length > 0) { console.log( `we detected multiple datapoints for the same group keys ${diffs.length}` @@ -280,8 +278,25 @@ export function to_time_series_data( return { group_info, num_of_dp: ts_list.length, + group_keys: Object.keys(group_info), + sub_keys, data: ts_list, }; }); return result; } + +export function to_table(data: any[], keys: string[], sub_keys: string[]) { + const tsd = groupByBenchmarkData(data, keys, sub_keys); + const result = tsd.map((group) => { + const group_info = group.group_info; + return { + group_info, + group_keys: Object.keys(group_info), + sub_keys: sub_keys, + num_of_dp: Object.values(group.rows).length, + data: group.rows, + }; + }); + return result; +} From ea96f8c9ce1ae30943611505c512e2b3ae6e0788 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Thu, 18 Sep 2025 01:35:01 -0400 Subject: [PATCH 12/13] add config --- .../ComparisonTableColumnRendering.tsx | 24 ++++++++++++++++--- .../v3/configs/helpers/RegressionPolicy.ts | 2 -- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableColumnRendering.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableColumnRendering.tsx index e02861125b..697348da54 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableColumnRendering.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableColumnRendering.tsx @@ -8,6 +8,7 @@ import { } from "@mui/x-data-grid"; import { BenchmarkComparisonPolicyConfig, + ComparisonResult, evaluateComparison, } from "components/benchmark/v3/configs/helpers/RegressionPolicy"; import { ComparisonTableConfig } from "../../../helper"; @@ -53,7 +54,7 @@ export function getComparisionTableConlumnRendering( const labelCol: GridColDef = { field: "label", headerName: "Label", - flex: 1.2, + width: 10, sortable: false, filterable: false, renderCell: (p) => ( @@ -152,13 +153,30 @@ export function ComparisonTableValueCell({ ? `${fmt(L)}→N/A` : fmt(L) === fmt(R) ? `${fmt(L)}` - : `${fmt(L)}→${fmt(R)} (${result.delta?.toFixed(2) ?? "N/A"})`; + : `${fmt(L)}→${fmt(R)}`; return ( - + {text} ); } + + +function renderComparisonResult( + result: ComparisonResult, +){ + return ( + {Object.entries(result).map(([key, value]) => ( + + {key}: {String(value)} + + ))} + ) +} diff --git a/torchci/components/benchmark/v3/configs/helpers/RegressionPolicy.ts b/torchci/components/benchmark/v3/configs/helpers/RegressionPolicy.ts index af711238c8..0fb7ca3983 100644 --- a/torchci/components/benchmark/v3/configs/helpers/RegressionPolicy.ts +++ b/torchci/components/benchmark/v3/configs/helpers/RegressionPolicy.ts @@ -80,7 +80,6 @@ export type ComparisonResult = { target: string; oldValue: number | null; newValue: number | null; - ratio: number | null; delta: number | null; verdict: ComparisonVerdict; reason?: string; @@ -103,7 +102,6 @@ export function evaluateComparison( const type: ComparisonPolicyType = policy.type ?? "ratio"; const base: ComparisonResult = { target: target ?? "general", - ratio: null, oldValue: oldValue ?? null, newValue: newValue ?? null, delta: oldValue != null && newValue != null ? newValue - oldValue : null, From ebc2d82a92903bd76dca9cb1205d2bca74fc9f16 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Thu, 18 Sep 2025 01:47:24 -0400 Subject: [PATCH 13/13] add config --- .../ComparisonTableColumnRendering.tsx | 19 +++++++---------- .../v3/configs/helpers/RegressionPolicy.ts | 21 +++++++++++++++---- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableColumnRendering.tsx b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableColumnRendering.tsx index 697348da54..0cf9fb38e6 100644 --- a/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableColumnRendering.tsx +++ b/torchci/components/benchmark/v3/components/dataRender/components/benchmarkTimeSeries/components/BenchmarkTimeSeriesComparisonSection/BenchmarkTimeSeriesComparisonTable/ComparisonTableColumnRendering.tsx @@ -157,26 +157,21 @@ export function ComparisonTableValueCell({ return ( - + {text} ); } - -function renderComparisonResult( - result: ComparisonResult, -){ - return ( +function renderComparisonResult(result: ComparisonResult) { + return ( + {Object.entries(result).map(([key, value]) => ( - + {key}: {String(value)} ))} - ) + + ); } diff --git a/torchci/components/benchmark/v3/configs/helpers/RegressionPolicy.ts b/torchci/components/benchmark/v3/configs/helpers/RegressionPolicy.ts index 0fb7ca3983..1f20be0097 100644 --- a/torchci/components/benchmark/v3/configs/helpers/RegressionPolicy.ts +++ b/torchci/components/benchmark/v3/configs/helpers/RegressionPolicy.ts @@ -125,20 +125,29 @@ export function evaluateComparison( direction: "up", }; const dir = rp.direction ?? "up"; + + const calculatedGood = rp.goodRatio + ? oldValue * rp.goodRatio + : oldValue * DEFAULT_GOOD_RATIO; + const calculatedBad = oldValue * rp.badRatio; // Compare with oldValue * ratio if (dir === "up") { if (rp.goodRatio != null && newValue > oldValue * rp.goodRatio) { return { ...base, verdict: "good", - reason: `new > old * goodRatio (${rp.goodRatio})`, + reason: `new > old * goodRatio (${ + rp.goodRatio + })[${calculatedGood.toFixed(2)}]`, }; } if (newValue < oldValue * rp.badRatio) { return { ...base, verdict: "regression", - reason: `new ≤ old * badRatio (${rp.badRatio})`, + reason: `new < old * badRatio (${ + rp.badRatio + })[${calculatedBad.toFixed(2)}]`, }; } return { @@ -151,14 +160,18 @@ export function evaluateComparison( return { ...base, verdict: "good", - reason: `new ≤ old * goodRatio (${rp.goodRatio})`, + reason: `new < old * goodRatio (${ + rp.goodRatio + })[${calculatedGood.toFixed(2)}}]`, }; } if (newValue > oldValue * rp.badRatio) { return { ...base, verdict: "regression", - reason: `new ≥ old * badRatio (${rp.badRatio})`, + reason: `new > old * badRatio (${ + rp.badRatio + })[${calculatedBad.toFixed(2)}]`, }; } return {