Skip to content

Commit 9704e41

Browse files
[HUD] SGLang vs vLLM Comparison Dashboard enhancements (#7135)
## Changes This PR includes the following changes in the comparison dashboard: - Hyperlink issue fix - Currently the hyperlink for the comparison dashboard was not working correctly, as it was redirecting to some other benchmark. - Adding a new dropdown option of `QPS` (Request rate), where defaults to `All QPS` and contains options which are dynamically fetched from the "model"'s benchmark data fetched from the clickhouse database. - Adding implementation logic for filtering the comparison data, based on the selected QPS before rendering graphs. - In the "Data details" table in the dashboard, once a model is selected, the "commit" column was empty. Fixed that bug to fetch the correct commit hash and display in the table. - Make sure that once a QPS is selected, all the corresponding data in that page (including the tables) is correctly populated based on that QPS only. - Whenever we visit the comparison dashboard, and no model is selected, we were earlier showing all the models present in both the repositories, but ideally we should show only those models which have data common to both SGLang and vLLM repos. Fixed that issue with these changes. ## Testing - Verified from the deployed dashboard: [link](https://torchci-d5hd5oow3-fbopensource.vercel.app/benchmark/llms?repos=vllm-project%2Fvllm%2Csgl-project%2Fsglang) - Hyperlink is working fine now in both cases (when no model is selected vs when a model is selected) - With model: [Hyperlink](https://torchci-d5hd5oow3-fbopensource.vercel.app/benchmark/llms?startTime=Wed%2C%2003%20Sep%202025%2006%3A58%3A42%20GMT&stopTime=Wed%2C%2010%20Sep%202025%2006%3A58%3A42%20GMT&granularity=day&lBranch=main&lCommit=&rBranch=main&rCommit=&repoName=pytorch%2Fpytorch&benchmarkName=&modelName=meta-llama%2FMeta-Llama-3.1-8B-Instruct&backendName=All%20Backends&modeName=All%20Modes&dtypeName=All%20DType&deviceName=All%20Devices&archName=All%20Platforms&repos=vllm-project%2Fvllm%2Csgl-project%2Fsglang&qps=All%20QPS) - Without model: [Hyperlink](https://torchci-d5hd5oow3-fbopensource.vercel.app/benchmark/llms?startTime=Wed%2C%2003%20Sep%202025%2006%3A58%3A42%20GMT&stopTime=Wed%2C%2010%20Sep%202025%2006%3A58%3A42%20GMT&granularity=day&lBranch=main&lCommit=&rBranch=main&rCommit=&repoName=pytorch%2Fpytorch&benchmarkName=&modelName=All%20Models&backendName=All%20Backends&modeName=All%20Modes&dtypeName=All%20DType&deviceName=All%20Devices&archName=All%20Platforms&repos=vllm-project%2Fvllm%2Csgl-project%2Fsglang&qps=All%20QPS) - Main comparison page showing only common models entries. <img width="1704" height="894" alt="Screenshot 2025-09-09 at 11 12 47 PM" src="https://github.com/user-attachments/assets/b577b828-6585-4ef5-bb8e-2b6d4017f77b" /> - QPS Filter enabled when a model is selected, and showing qps values that are fetched dynamically for that particular model. <img width="1704" height="894" alt="Screenshot 2025-09-09 at 11 13 57 PM" src="https://github.com/user-attachments/assets/670ee1e8-e01c-4cc6-aa07-29bf541b88a2" /> - When a certain QPS Filter is selected, then only those records should be rendered in that dashboard page. <img width="1704" height="894" alt="Screenshot 2025-09-09 at 11 15 09 PM" src="https://github.com/user-attachments/assets/d20248d0-eadb-4f83-ae73-8275d65d7f92" /> - Data details table showing the "commit" column correctly. <img width="1704" height="861" alt="Screenshot 2025-09-09 at 11 15 30 PM" src="https://github.com/user-attachments/assets/d0a14409-5181-45ff-8384-20857af726df" />
1 parent ebb706a commit 9704e41

10 files changed

Lines changed: 398 additions & 172 deletions

File tree

torchci/clickhouse_queries/oss_ci_benchmark_llms/params.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
"backends": "Array(String)",
1414
"repo": "String",
1515
"startTime": "DateTime64(3)",
16-
"stopTime": "DateTime64(3)"
16+
"stopTime": "DateTime64(3)",
17+
"requestRate": "String"
1718
},
1819
"tests": [
1920
{
@@ -30,7 +31,8 @@
3031
"backends": [],
3132
"repo": "pytorch/pytorch",
3233
"startTime": "2025-01-28T22:46:18.214",
33-
"stopTime": "2025-02-04T22:46:18.214"
34+
"stopTime": "2025-02-04T22:46:18.214",
35+
"requestRate": ""
3436
}
3537
]
3638
}

torchci/clickhouse_queries/oss_ci_benchmark_llms/query.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ WITH benchmarks AS (
133133
NOT has({excludedMetrics: Array(String) }, o.metric.'name')
134134
OR empty({excludedMetrics: Array(String) })
135135
)
136+
AND (
137+
JSONExtractString(
138+
tupleElement(o.benchmark, 'extra_info')['args'],
139+
'request_rate'
140+
) = {requestRate: String}
141+
OR {requestRate: String} = ''
142+
)
136143
AND notEmpty(o.metric.'name')
137144
)
138145

torchci/components/benchmark/llms/LLMsBenchmarkPage.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
DEFAULT_DTYPE_NAME,
2222
DEFAULT_MODE_NAME,
2323
DEFAULT_MODEL_NAME,
24+
DEFAULT_QPS_NAME,
2425
HELION_BENCHMARK_NAME,
2526
REPO_TO_BENCHMARKS,
2627
} from "lib/benchmark/llms/common";
@@ -52,6 +53,7 @@ export default function LLMsBenchmarkPage() {
5253
dtypeName: DEFAULT_DTYPE_NAME,
5354
deviceName: DEFAULT_DEVICE_NAME,
5455
archName: DEFAULT_ARCH_NAME,
56+
qps: DEFAULT_QPS_NAME,
5557
startTime: defaultStartTime,
5658
stopTime: defaultStopTime,
5759
timeRange: LAST_N_DAYS,

torchci/components/benchmark/llms/LLMsComparingBenchmarkPage.tsx

Lines changed: 164 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,19 @@ import {
2121
DEFAULT_DTYPE_NAME,
2222
DEFAULT_MODE_NAME,
2323
DEFAULT_MODEL_NAME,
24+
DEFAULT_QPS_NAME,
2425
HELION_BENCHMARK_NAME,
2526
LLM_BENCHMARK_CONFIG_QUERY,
27+
LLM_BENCHMARK_DATA_QUERY,
2628
REPO_TO_BENCHMARKS,
2729
} from "lib/benchmark/llms/common";
2830
import { LLMsBenchmarkMode } from "lib/benchmark/llms/types/benchmarkMode";
31+
import { DropdownGroupItemType } from "lib/benchmark/llms/types/dashboardPickerTypes";
2932
import { LLMsBenchmarkProps } from "lib/benchmark/llms/types/dashboardProps";
3033
import { getBenchmarkDropdownFeatures } from "lib/benchmark/llms/utils/dashboardPickerUtils";
3134
import {
32-
fetchBenchmarkDataForRepos,
3335
getLLMsBenchmarkPropsQueryParameter,
36+
useBenchmarkDataForRepos,
3437
} from "lib/benchmark/llms/utils/llmUtils";
3538
import { LLMsDashboardPicker } from "./components/dashboardPicker/LLMsDashboardPicker";
3639
import { LLMsTimeRangePicker } from "./components/dashboardPicker/LLMsTimeRangePicker";
@@ -51,6 +54,7 @@ export default function LLMsComparingBenchmarkPage() {
5154
dtypeName: DEFAULT_DTYPE_NAME,
5255
deviceName: DEFAULT_DEVICE_NAME,
5356
archName: DEFAULT_ARCH_NAME,
57+
qps: DEFAULT_QPS_NAME,
5458
startTime: defaultStartTime,
5559
stopTime: defaultStopTime,
5660
timeRange: LAST_N_DAYS,
@@ -114,14 +118,46 @@ const MainPageForComparison = ({
114118
dispatch: React.Dispatch<any>;
115119
}) => {
116120
const [baseUrl, setBaseUrl] = useState<string>("");
117-
const [allRepoData, setAllRepoData] = useState<any[]>([]);
118-
const [allRepoErrors, setAllRepoErrors] = useState<any[]>([]);
119-
const [isLoading, setIsLoading] = useState<boolean>(true);
121+
const [qpsOptions, setQpsOptions] = useState<string[]>([]);
122+
const [modelQpsMap, setModelQpsMap] = useState<Record<string, string[]>>({});
120123
const queryParams = useMemo(
121124
() => getLLMsBenchmarkPropsQueryParameter(props),
122125
[props]
123126
);
124127

128+
const repoQueryParams = useMemo(
129+
() =>
130+
props.repos.map((repo) => {
131+
const repoSpecificProps = { ...props, repoName: repo, repos: [] };
132+
return getLLMsBenchmarkPropsQueryParameter(repoSpecificProps);
133+
}),
134+
[
135+
props.repos,
136+
props.benchmarkName,
137+
props.modelName,
138+
props.backendName,
139+
props.modeName,
140+
props.dtypeName,
141+
props.deviceName,
142+
props.archName,
143+
props.startTime,
144+
props.stopTime,
145+
props.granularity,
146+
props.lCommit,
147+
props.rCommit,
148+
props.lBranch,
149+
props.rBranch,
150+
]
151+
);
152+
153+
const { data: configResults } = useBenchmarkDataForRepos(
154+
LLM_BENCHMARK_CONFIG_QUERY,
155+
repoQueryParams
156+
);
157+
const allRepoData = configResults?.map((r: any) => r.data) || [];
158+
const allRepoErrors = configResults?.map((r: any) => r.error) || [];
159+
const isLoading = !configResults;
160+
125161
useEffect(() => {
126162
const newProps = resetProps(
127163
router.query,
@@ -137,44 +173,101 @@ const MainPageForComparison = ({
137173
);
138174
}, [router.query]);
139175

176+
// Data fetching handled by useBenchmarkDataForRepos
177+
178+
const modelQpsQueryParams = useMemo(
179+
() =>
180+
props.repos.map((repo) => {
181+
const repoSpecificProps = {
182+
...props,
183+
repoName: repo,
184+
repos: [],
185+
modelName: DEFAULT_MODEL_NAME,
186+
qps: DEFAULT_QPS_NAME,
187+
};
188+
return getLLMsBenchmarkPropsQueryParameter(repoSpecificProps);
189+
}),
190+
[
191+
props.repos,
192+
props.backendName,
193+
props.modeName,
194+
props.dtypeName,
195+
props.deviceName,
196+
props.archName,
197+
props.startTime,
198+
props.stopTime,
199+
props.benchmarkName,
200+
props.granularity,
201+
props.rBranch,
202+
props.rCommit,
203+
]
204+
);
205+
206+
const modelQpsParamsWithBranch = useMemo(
207+
() =>
208+
modelQpsQueryParams.map((qp) => ({
209+
...qp,
210+
branches: props.rBranch ? [props.rBranch] : [],
211+
commits: props.rCommit ? [props.rCommit] : [],
212+
})),
213+
[modelQpsQueryParams, props.rBranch, props.rCommit]
214+
);
215+
216+
const { data: modelQpsResults } = useBenchmarkDataForRepos(
217+
LLM_BENCHMARK_DATA_QUERY,
218+
modelQpsParamsWithBranch
219+
);
220+
140221
useEffect(() => {
141-
let cancelled = false;
142-
const repoQueryParams = props.repos.map((repo) => {
143-
const repoSpecificProps = { ...props, repoName: repo };
144-
return getLLMsBenchmarkPropsQueryParameter(repoSpecificProps);
145-
});
146-
setIsLoading(true);
147-
fetchBenchmarkDataForRepos(
148-
LLM_BENCHMARK_CONFIG_QUERY,
149-
repoQueryParams
150-
).then((results) => {
151-
if (cancelled) {
152-
return;
153-
}
154-
setAllRepoData(results.map((r) => r.data));
155-
setAllRepoErrors(results.map((r) => r.error));
156-
setIsLoading(false);
222+
if (!modelQpsResults) {
223+
return;
224+
}
225+
const map: Record<string, string[]> = {};
226+
modelQpsResults.forEach((r: any) => {
227+
const data = (r.data || []) as any[];
228+
const grouped = _.groupBy(data, (rec) => rec.model);
229+
Object.entries(grouped).forEach(([model, recs]) => {
230+
const qpsValues = _.uniq(
231+
recs
232+
.map((rec: any) => rec.extra?.request_rate)
233+
.filter(
234+
(v): v is string | number =>
235+
v !== undefined &&
236+
v !== null &&
237+
v !== "" &&
238+
(v === "inf" || !isNaN(Number(v)))
239+
)
240+
.map((v) => (v === "inf" ? "inf" : String(Number(v))))
241+
);
242+
map[model] = _.uniq([...(map[model] || []), ...qpsValues]);
243+
});
157244
});
158-
return () => {
159-
cancelled = true;
160-
};
161-
}, [
162-
props.repos,
163-
props.benchmarkName,
164-
props.modelName,
165-
props.backendName,
166-
props.modeName,
167-
props.dtypeName,
168-
props.deviceName,
169-
props.archName,
170-
props.startTime,
171-
props.stopTime,
172-
props.granularity,
173-
props.lCommit,
174-
props.rCommit,
175-
props.lBranch,
176-
props.rBranch,
177-
]);
245+
Object.keys(map).forEach((m) =>
246+
map[m].sort(
247+
(a, b) =>
248+
(a === "inf" ? Infinity : Number(a)) -
249+
(b === "inf" ? Infinity : Number(b))
250+
)
251+
);
252+
setModelQpsMap(map);
253+
}, [modelQpsResults]);
254+
255+
useEffect(() => {
256+
if (props.modelName === DEFAULT_MODEL_NAME) {
257+
setQpsOptions([]);
258+
dispatch({ type: "UPDATE_FIELD", field: "qps", value: DEFAULT_QPS_NAME });
259+
return;
260+
}
261+
const shared = modelQpsMap[props.modelName] || [];
262+
setQpsOptions([DEFAULT_QPS_NAME, ...shared]);
263+
if (!shared.includes(props.qps)) {
264+
dispatch({
265+
type: "UPDATE_FIELD",
266+
field: "qps",
267+
value: DEFAULT_QPS_NAME,
268+
});
269+
}
270+
}, [props.modelName, modelQpsMap]);
178271

179272
const hasError = allRepoErrors.some((error) => error);
180273
if (hasError) {
@@ -223,7 +316,7 @@ const MainPageForComparison = ({
223316
);
224317
}
225318

226-
const combinedData = allRepoData.flatMap((repoData, index) => {
319+
let combinedData = allRepoData.flatMap((repoData, index) => {
227320
const repo = props.repos[index];
228321
return repoData.map((dataItem: any) => ({
229322
...dataItem,
@@ -255,6 +348,26 @@ const MainPageForComparison = ({
255348
};
256349
});
257350

351+
if (qpsOptions.length > 1) {
352+
dropdownMapList.push({
353+
type: DropdownGroupItemType.Qps,
354+
labelName: "QPS",
355+
options: qpsOptions,
356+
});
357+
}
358+
359+
if (props.modelName === DEFAULT_MODEL_NAME) {
360+
const modelDropdown = dropdownMapList.find(
361+
(d) => d.type === DropdownGroupItemType.ModelName
362+
);
363+
if (modelDropdown) {
364+
const sharedModels = modelDropdown.options.slice(1);
365+
combinedData = combinedData.filter((d: any) =>
366+
sharedModels.includes(d.model)
367+
);
368+
}
369+
}
370+
258371
const metricNames = getMetricNames(combinedData);
259372
// Default to latest for Helion Benchmark, otherwise default to oldest commit
260373
const lcommitFallbackIdx =
@@ -364,6 +477,11 @@ function resetProps(
364477
newProps.archName = archName;
365478
}
366479

480+
const qps: string = (urlQuery.qps as string) ?? undefined;
481+
if (qps !== undefined) {
482+
newProps.qps = qps;
483+
}
484+
367485
const lBranch: string = (urlQuery.lBranch as string) ?? undefined;
368486
if (lBranch !== undefined) {
369487
newProps.lBranch = lBranch;
@@ -435,7 +553,11 @@ const formLink = (props: LLMsBenchmarkProps, baseUrl: string) => {
435553
props.dtypeName
436554
)}&deviceName=${encodeURIComponent(
437555
props.deviceName
438-
)}&archName=${encodeURIComponent(props.archName)}`}
556+
)}&archName=${encodeURIComponent(
557+
props.archName
558+
)}&repos=${encodeURIComponent(
559+
props.repos.join(",")
560+
)}&qps=${encodeURIComponent(props.qps)}`}
439561
/>
440562
);
441563
};

0 commit comments

Comments
 (0)