Skip to content

Commit ea806cc

Browse files
authored
Merge pull request #34 from InfiniTensor/feat/add_hardware_tab
feat: Add hardware tab in dashboard
2 parents 2910b58 + 2354183 commit ea806cc

5 files changed

Lines changed: 417 additions & 29 deletions

File tree

dashboard/app.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,9 @@ def _latest(lst):
214214
latest_infer = _latest(infer_runs)
215215
latest_ops = _latest(ops_runs)
216216
latest_train = _latest(train_runs)
217+
latest_hw = _latest(hw_runs)
217218

218-
colA, colB, colC, colD = st.columns(4)
219+
colA, colB, colC, colD, colE = st.columns(5)
219220

220221
with colA:
221222
st.markdown("#### 🔗 通信(最新)")
@@ -255,6 +256,15 @@ def _latest(lst):
255256
st.write(f"- time: {latest_train.get('time','')}")
256257
st.write(f"- status: {'✅' if latest_train.get('success') else '❌'}")
257258

259+
with colE:
260+
st.markdown("#### 🔧 硬件(最新)")
261+
if not latest_hw:
262+
st.info("暂无硬件结果")
263+
else:
264+
st.write(f"- testcase: `{latest_hw.get('testcase','')}`")
265+
st.write(f"- time: {latest_hw.get('time','')}")
266+
st.write(f"- status: {'✅' if latest_hw.get('success') else '❌'}")
267+
258268
st.divider()
259269

260270
# ========== Recent runs table ==========
@@ -311,7 +321,7 @@ def _latest(lst):
311321
st.markdown("---")
312322
st.markdown("### 🚀 快速导航")
313323

314-
col1, col2, col3, col4 = st.columns(4)
324+
col1, col2, col3, col4, col5 = st.columns(5)
315325
if col1.button("🔗 通信测试分析", use_container_width=True):
316326
st.switch_page("pages/communication.py")
317327
if col2.button("⚡ 算子测试分析", use_container_width=True):
@@ -320,6 +330,8 @@ def _latest(lst):
320330
st.switch_page("pages/inference.py")
321331
if col4.button("🏋️ 训练测试分析", use_container_width=True):
322332
st.switch_page("pages/training.py")
333+
if col5.button("🔧 硬件测试分析", use_container_width=True):
334+
st.switch_page("pages/hardware.py")
323335

324336
except Exception as e:
325337
st.error(f"Dashboard 加载失败: {e}")

dashboard/pages/hardware.py

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
#!/usr/bin/env python3
2+
"""Hardware tests analysis page."""
3+
4+
import streamlit as st
5+
import pandas as pd
6+
7+
from common import init_page, show_data_source_info
8+
from components.header import render_header
9+
from utils.visualizations import (
10+
create_summary_table_hw,
11+
plot_hw_mem_sweep,
12+
plot_hw_cache,
13+
)
14+
15+
init_page("硬件测试分析 | InfiniMetrics", "🔧")
16+
17+
18+
def main():
19+
render_header()
20+
st.markdown("## 🔧 硬件性能测试分析")
21+
22+
show_data_source_info()
23+
24+
runs = st.session_state.data_loader.list_test_runs()
25+
# Identify hardware runs by testcase starting with hardware
26+
hw_runs = [r for r in runs if (r.get("testcase") or "").startswith("hardware")]
27+
28+
if not hw_runs:
29+
st.info("未找到硬件测试结果(testcase 需以 hardware.* 开头)。")
30+
return
31+
32+
# ---------- Sidebar Filters ----------
33+
with st.sidebar:
34+
st.markdown("### 🔍 筛选条件")
35+
only_success = st.checkbox("仅显示成功测试", value=True)
36+
y_log = st.checkbox("Y轴对数刻度(可选)", value=False)
37+
38+
filtered = [r for r in hw_runs if (not only_success or r.get("success"))]
39+
40+
st.caption(f"找到 {len(filtered)} 个硬件测试")
41+
42+
if not filtered:
43+
st.warning("没有符合条件的测试结果")
44+
return
45+
46+
# ---------- Run Selection ----------
47+
options = {
48+
f"{r.get('testcase','unknown')} | {r.get('time','')} | {r.get('run_id','')[:12]}": i
49+
for i, r in enumerate(filtered)
50+
}
51+
52+
selected = st.multiselect(
53+
"选择要分析的测试运行(可多选对比)",
54+
list(options.keys()),
55+
default=list(options.keys())[:1],
56+
)
57+
if not selected:
58+
return
59+
60+
def _load_run_data(run_info):
61+
"""Load test result data for a run."""
62+
identifier = run_info.get("path") or run_info.get("run_id")
63+
return {
64+
**run_info,
65+
"data": st.session_state.data_loader.load_test_result(identifier),
66+
}
67+
68+
selected_runs = [_load_run_data(filtered[options[k]]) for k in selected]
69+
70+
tab1, tab2, tab3 = st.tabs(["📈 性能图表", "📊 数据表格", "🔍 详细配置"])
71+
72+
# ---------- Charts ----------
73+
with tab1:
74+
for run in selected_runs:
75+
metrics = run["data"].get("metrics", [])
76+
77+
# Group metrics by type
78+
mem_metrics = [m for m in metrics if "mem_sweep" in m.get("name", "")]
79+
cache_metrics = [m for m in metrics if "cache" in m.get("name", "")]
80+
stream_metrics = [m for m in metrics if "stream" in m.get("name", "")]
81+
82+
st.markdown(f"### {run.get('run_id', '')[:16]}")
83+
84+
# Memory bandwidth plots
85+
if mem_metrics:
86+
st.markdown("#### 内存带宽 (Memory Sweep)")
87+
cols = st.columns(min(3, len(mem_metrics)))
88+
for i, m in enumerate(mem_metrics):
89+
with cols[i % len(cols)]:
90+
df = m.get("data")
91+
if df is not None and len(df.columns) >= 2:
92+
fig = plot_hw_mem_sweep(
93+
df,
94+
title=m.get("name", "memory"),
95+
y_log_scale=y_log,
96+
)
97+
st.plotly_chart(fig, use_container_width=True)
98+
99+
# Cache bandwidth plots
100+
if cache_metrics:
101+
st.markdown("#### 缓存带宽 (Cache)")
102+
cols = st.columns(min(2, len(cache_metrics)))
103+
for i, m in enumerate(cache_metrics):
104+
with cols[i % len(cols)]:
105+
df = m.get("data")
106+
if df is not None and len(df.columns) >= 2:
107+
fig = plot_hw_cache(
108+
df,
109+
title=m.get("name", "cache"),
110+
y_log_scale=y_log,
111+
)
112+
st.plotly_chart(fig, use_container_width=True)
113+
114+
# STREAM benchmark scalars
115+
if stream_metrics:
116+
st.markdown("#### STREAM 基准测试")
117+
stream_data = []
118+
for m in stream_metrics:
119+
stream_data.append(
120+
{
121+
"指标": m.get("name", ""),
122+
"数值": f"{m.get('value', 0):.2f} {m.get('unit', '')}",
123+
}
124+
)
125+
if stream_data:
126+
st.dataframe(
127+
pd.DataFrame(stream_data),
128+
use_container_width=True,
129+
hide_index=True,
130+
)
131+
132+
# ---------- Tables ----------
133+
with tab2:
134+
for run in selected_runs:
135+
with st.expander(f"{run.get('run_id')} - 原始数据"):
136+
for m in run["data"].get("metrics", []):
137+
if m.get("data") is None:
138+
continue
139+
st.markdown(f"**{m.get('name')}**")
140+
st.dataframe(m["data"], use_container_width=True, hide_index=True)
141+
142+
# ---------- Config ----------
143+
with tab3:
144+
for run in selected_runs:
145+
with st.expander(f"{run.get('run_id')} - 配置与环境"):
146+
summary = create_summary_table_hw(run["data"])
147+
st.dataframe(summary, use_container_width=True, hide_index=True)
148+
st.markdown("**config**")
149+
st.json(run["data"].get("config", {}))
150+
st.markdown("**environment**")
151+
st.json(run["data"].get("environment", {}))
152+
153+
154+
if __name__ == "__main__":
155+
main()
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/usr/bin/env python3
2+
"""Visualization functions for InfiniMetrics dashboard.
3+
4+
This package provides visualization utilities organized by test type:
5+
- base: Common/legacy visualization functions
6+
- hardware: Hardware test visualizations (memory sweep, cache bandwidth)
7+
- (future) communication: Communication test visualizations
8+
- (future) inference: Inference test visualizations
9+
- (future) operator: Operator test visualizations
10+
"""
11+
12+
from .base import (
13+
plot_metric_vs_size,
14+
plot_comparison_matrix,
15+
create_summary_table,
16+
create_gauge_chart,
17+
plot_timeseries_auto,
18+
create_summary_table_infer,
19+
create_summary_table_ops,
20+
)
21+
from .hardware import (
22+
create_summary_table_hw,
23+
plot_hw_mem_sweep,
24+
plot_hw_cache,
25+
)
26+
27+
__all__ = [
28+
# Base (common/legacy)
29+
"plot_metric_vs_size",
30+
"plot_comparison_matrix",
31+
"create_summary_table",
32+
"create_gauge_chart",
33+
"plot_timeseries_auto",
34+
"create_summary_table_infer",
35+
"create_summary_table_ops",
36+
# Hardware
37+
"create_summary_table_hw",
38+
"plot_hw_mem_sweep",
39+
"plot_hw_cache",
40+
]

0 commit comments

Comments
 (0)