Skip to content

Commit e094fce

Browse files
authored
[Benchmark] Reorg and Clean LLMs component (#6351)
Issue: #6323 - Use UserReducer to handle the benchmark dashboard props - Restruct the LLMsPage, move picker logics to UI component, and render the dropdown list dynamically. - Pass props instead of each param to llmsReport for easy maintainance Demo: https://torchci-4sjlqbjzx-fbopensource.vercel.app/benchmark/llms?repoName=pytorch%2Fexecutorch Next steps: - keep cleaning the rest of component - introduct repo speciifc configuration logic
1 parent e1b6b59 commit e094fce

File tree

16 files changed

+787
-482
lines changed

16 files changed

+787
-482
lines changed
Lines changed: 349 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,349 @@
1+
import { Stack, Typography } from "@mui/material";
2+
import {
3+
DEFAULT_REPO_NAME,
4+
LAST_N_DAYS,
5+
MAIN_BRANCH,
6+
} from "components/benchmark/common";
7+
import CopyLink from "components/CopyLink";
8+
import { Granularity } from "components/metrics/panels/TimeSeriesPanel";
9+
import dayjs from "dayjs";
10+
import _, { cloneDeep } from "lodash";
11+
import { NextRouter, useRouter } from "next/router";
12+
import { ParsedUrlQuery } from "querystring";
13+
import { useEffect, useReducer, useState } from "react";
14+
import { propsReducer } from "./context/BenchmarkProps";
15+
16+
import LoadingPage from "components/LoadingPage";
17+
import {
18+
DEFAULT_ARCH_NAME,
19+
DEFAULT_BACKEND_NAME,
20+
DEFAULT_DEVICE_NAME,
21+
DEFAULT_DTYPE_NAME,
22+
DEFAULT_MODE_NAME,
23+
DEFAULT_MODEL_NAME,
24+
REPO_TO_BENCHMARKS,
25+
} from "lib/benchmark/llms/common";
26+
import { LLMsBenchmarkProps } from "lib/benchmark/llms/types/dashboardProps";
27+
import { getBenchmarkDropdownFeatures } from "lib/benchmark/llms/utils/dashboardPickerUtils";
28+
import {
29+
getLLMsBenchmarkPropsQueryParameter,
30+
useBenchmarkPropsData,
31+
} from "lib/benchmark/llms/utils/llmUtils";
32+
import { LLMsDashboardPicker } from "./components/dashboardPicker/LLMsDashboardPicker";
33+
import { LLMsTimeRangePicker } from "./components/dashboardPicker/LLMsTimeRangePicker";
34+
import LLMsReport from "./components/LLMsReport";
35+
36+
export default function LLMsBenchmarkPage() {
37+
const router = useRouter();
38+
39+
// Set the default start and stop time to be the last N days when the page is loaded
40+
const defaultStartTime = dayjs().subtract(LAST_N_DAYS, "day");
41+
const defaultStopTime = dayjs();
42+
43+
const initialPropsState: LLMsBenchmarkProps = {
44+
repoName: DEFAULT_REPO_NAME,
45+
benchmarkName: "",
46+
modelName: DEFAULT_MODEL_NAME,
47+
backendName: DEFAULT_BACKEND_NAME,
48+
modeName: DEFAULT_MODE_NAME,
49+
dtypeName: DEFAULT_DTYPE_NAME,
50+
deviceName: DEFAULT_DEVICE_NAME,
51+
archName: DEFAULT_ARCH_NAME,
52+
startTime: defaultStartTime,
53+
stopTime: defaultStopTime,
54+
timeRange: LAST_N_DAYS,
55+
granularity: "day",
56+
lCommit: "",
57+
rCommit: "",
58+
lBranch: MAIN_BRANCH,
59+
rBranch: MAIN_BRANCH,
60+
};
61+
62+
const [props, dispatch] = useReducer(propsReducer, initialPropsState);
63+
64+
// pass initial state in runtime for benchmark props
65+
return (
66+
<MainPage
67+
props={props}
68+
dispatch={dispatch}
69+
defaultStartTime={defaultStartTime}
70+
defaultStopTime={defaultStopTime}
71+
router={router}
72+
/>
73+
);
74+
}
75+
76+
// render the page before the data is loaded or when an error occured
77+
const PrefetchRender = ({
78+
children,
79+
props,
80+
dispatch,
81+
baseUrl,
82+
}: {
83+
children: any;
84+
props: LLMsBenchmarkProps;
85+
dispatch: React.Dispatch<any>;
86+
baseUrl: string;
87+
}) => {
88+
return (
89+
<div>
90+
<Stack direction="row" spacing={2} sx={{ mb: 2 }}>
91+
{getBenchmarkName(props.benchmarkName, props.repoName)}
92+
{formLink(props, baseUrl)}
93+
</Stack>
94+
<Stack direction="row" spacing={2} sx={{ mb: 2 }}>
95+
<LLMsTimeRangePicker props={props} dispatch={dispatch} />
96+
</Stack>
97+
<Stack>{children}</Stack>
98+
</div>
99+
);
100+
};
101+
102+
/**
103+
* @returns Main page for the LLMs dashboard
104+
* the page is routed in pagesM/bencmark/llms.tsx
105+
*/
106+
const MainPage = ({
107+
defaultStartTime,
108+
defaultStopTime,
109+
props,
110+
dispatch,
111+
router,
112+
}: {
113+
defaultStartTime: dayjs.Dayjs;
114+
defaultStopTime: dayjs.Dayjs;
115+
router: NextRouter;
116+
props: LLMsBenchmarkProps;
117+
dispatch: React.Dispatch<any>;
118+
}) => {
119+
const [baseUrl, setBaseUrl] = useState<string>("");
120+
useEffect(() => {
121+
const newProps = resetProps(
122+
router.query,
123+
props,
124+
defaultStartTime,
125+
defaultStopTime
126+
);
127+
dispatch({ type: "UPDATE_FIELDS", payload: newProps });
128+
setBaseUrl(
129+
`${window.location.protocol}//${
130+
window.location.host
131+
}${router.asPath.replace(/\?.+/, "")}`
132+
);
133+
}, [router.query]);
134+
const queryParams = getLLMsBenchmarkPropsQueryParameter(props);
135+
const { data, error, isLoading } = useBenchmarkPropsData(queryParams);
136+
137+
// an error occured while fetching the benchmark props data
138+
// give user choice for time range picker
139+
if (error) {
140+
return (
141+
<PrefetchRender props={props} dispatch={dispatch} baseUrl={baseUrl}>
142+
<>
143+
Error loading data for{" "}
144+
{(props.benchmarkName
145+
? [props.benchmarkName]
146+
: REPO_TO_BENCHMARKS[props.repoName]
147+
).join(", ")}
148+
, please select different time range, if this happens again, please
149+
reach out to the pytorch team.
150+
</>
151+
</PrefetchRender>
152+
);
153+
}
154+
155+
// the benchmark props data is stil loading
156+
if (!data && isLoading) {
157+
return (
158+
<div>
159+
<PrefetchRender props={props} dispatch={dispatch} baseUrl={baseUrl}>
160+
<>
161+
Loading data for{" "}
162+
{(props.benchmarkName
163+
? [props.benchmarkName]
164+
: REPO_TO_BENCHMARKS[props.repoName]
165+
).join(", ")}
166+
, please wait a min
167+
</>
168+
</PrefetchRender>
169+
<div>
170+
<LoadingPage />
171+
</div>
172+
</div>
173+
);
174+
}
175+
176+
// no prop data found for the given time range
177+
if (data.length === 0) {
178+
return (
179+
<PrefetchRender props={props} dispatch={dispatch} baseUrl={baseUrl}>
180+
<>
181+
Found no records for{" "}
182+
{(props.benchmarkName
183+
? [props.benchmarkName]
184+
: REPO_TO_BENCHMARKS[props.repoName]
185+
).join(", ")}
186+
, please select different time range
187+
</>
188+
</PrefetchRender>
189+
);
190+
}
191+
192+
const options = data;
193+
const dropdownMapList = getBenchmarkDropdownFeatures(options, props.repoName);
194+
const metricNames = getMetricNames(data);
195+
return (
196+
<div>
197+
<Stack direction="row" spacing={2} sx={{ mb: 2 }}>
198+
{getBenchmarkName(props.benchmarkName, props.repoName)}
199+
{formLink(props, baseUrl)}
200+
</Stack>
201+
<LLMsDashboardPicker
202+
options={dropdownMapList}
203+
props={props}
204+
dispatch={dispatch}
205+
queryParams={queryParams}
206+
/>
207+
<LLMsReport
208+
props={props}
209+
metricNames={metricNames}
210+
benchmarkPropsQueryParams={queryParams}
211+
/>
212+
</div>
213+
);
214+
};
215+
216+
function getMetricNames(data: any) {
217+
const metricNames = _.uniq(data.map((r: any) => r.metric));
218+
return metricNames as string[];
219+
}
220+
221+
function resetProps(
222+
urlQuery: ParsedUrlQuery,
223+
prevProps: any,
224+
defaultStartTime: dayjs.Dayjs,
225+
defaultStopTime: dayjs.Dayjs
226+
) {
227+
const newProps = cloneDeep(prevProps);
228+
const startTime: string = (urlQuery.startTime as string) ?? undefined;
229+
230+
if (startTime !== undefined) {
231+
newProps.startTime = dayjs(startTime);
232+
if (dayjs(startTime).valueOf() !== defaultStartTime.valueOf()) {
233+
newProps.timeRange = -1;
234+
}
235+
}
236+
const stopTime: string = (urlQuery.stopTime as string) ?? undefined;
237+
if (stopTime !== undefined) {
238+
newProps.stopTime = dayjs(stopTime);
239+
if (dayjs(stopTime).valueOf() !== defaultStopTime.valueOf()) {
240+
newProps.timeRange = -1;
241+
}
242+
}
243+
244+
const granularity: Granularity =
245+
(urlQuery.granularity as Granularity) ?? undefined;
246+
if (granularity !== undefined) {
247+
newProps.granularity = granularity;
248+
}
249+
250+
const repoName: string = (urlQuery.repoName as string) ?? undefined;
251+
if (repoName !== undefined && repoName) {
252+
newProps.repoName = repoName;
253+
}
254+
255+
const benchmarkName: string = (urlQuery.benchmarkName as string) ?? undefined;
256+
if (benchmarkName != undefined) {
257+
newProps.benchmarkName = benchmarkName;
258+
}
259+
260+
const modelName: string = (urlQuery.modelName as string) ?? undefined;
261+
if (modelName !== undefined) {
262+
newProps.modelName = modelName;
263+
}
264+
265+
const backendName: string = (urlQuery.backendName as string) ?? undefined;
266+
if (backendName !== undefined) {
267+
newProps.backendName = backendName;
268+
}
269+
270+
const modeName: string = (urlQuery.modeName as string) ?? undefined;
271+
if (modeName !== undefined) {
272+
newProps.modeName = modeName;
273+
}
274+
275+
const dtypeName: string = (urlQuery.dtypeName as string) ?? undefined;
276+
if (dtypeName !== undefined) {
277+
newProps.dtypeName = dtypeName;
278+
}
279+
280+
const deviceName: string = (urlQuery.deviceName as string) ?? undefined;
281+
if (deviceName !== undefined) {
282+
newProps.deviceName = deviceName;
283+
}
284+
285+
// Set the default arch to Android for ExecuTorch as it has only 2 options Android and iOS
286+
const archName: string = (urlQuery.archName as string) ?? undefined;
287+
if (archName !== undefined) {
288+
newProps.archName = archName;
289+
}
290+
291+
const lBranch: string = (urlQuery.lBranch as string) ?? undefined;
292+
if (lBranch !== undefined) {
293+
newProps.lBranch = lBranch;
294+
}
295+
296+
const lCommit: string = (urlQuery.lCommit as string) ?? undefined;
297+
if (lCommit !== undefined) {
298+
newProps.lCommit = lCommit;
299+
}
300+
301+
const rBranch: string = (urlQuery.rBranch as string) ?? undefined;
302+
if (rBranch !== undefined) {
303+
newProps.rBranch = rBranch;
304+
}
305+
306+
const rCommit: string = (urlQuery.rCommit as string) ?? undefined;
307+
if (rCommit !== undefined) {
308+
newProps.rCommit = rCommit;
309+
}
310+
return newProps;
311+
}
312+
313+
const getBenchmarkName = (benchmarkName: string | any, repoName: string) => {
314+
return (
315+
<Typography fontSize={"2rem"} fontWeight={"bold"}>
316+
{benchmarkName ? benchmarkName : REPO_TO_BENCHMARKS[repoName]} dashboard
317+
</Typography>
318+
);
319+
};
320+
321+
const formLink = (props: LLMsBenchmarkProps, baseUrl: string) => {
322+
return (
323+
<CopyLink
324+
textToCopy={`${baseUrl}?startTime=${encodeURIComponent(
325+
props.startTime.toString()
326+
)}&stopTime=${encodeURIComponent(
327+
props.stopTime.toString()
328+
)}&granularity=${props.granularity}&lBranch=${props.lBranch}&lCommit=${
329+
props.lCommit
330+
}&rBranch=${props.rBranch}&rCommit=${
331+
props.rCommit
332+
}&repoName=${encodeURIComponent(
333+
props.repoName
334+
)}&benchmarkName=${encodeURIComponent(
335+
props.benchmarkName
336+
)}&modelName=${encodeURIComponent(
337+
props.modelName
338+
)}&backendName=${encodeURIComponent(
339+
props.backendName
340+
)}&modeName=${encodeURIComponent(
341+
props.modeName
342+
)}&dtypeName=${encodeURIComponent(
343+
props.dtypeName
344+
)}&deviceName=${encodeURIComponent(
345+
props.deviceName
346+
)}&archName=${encodeURIComponent(props.archName)}`}
347+
/>
348+
);
349+
};

0 commit comments

Comments
 (0)