|
| 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