| <template> |
| <div class="chart-wrapper"> |
| <v-chart :option="chartOption" :autoresize="true" style="height: 100%; width: 100%" /> |
| </div> |
| </template> |
|
|
| <script setup> |
| import { computed } from 'vue'; |
| import { removeSGLangPrefix } from '../utils/dataProcessor'; |
| import { use } from 'echarts/core'; |
| import { CanvasRenderer } from 'echarts/renderers'; |
| import { BarChart, LineChart } from 'echarts/charts'; |
| import { |
| TitleComponent, |
| TooltipComponent, |
| LegendComponent, |
| GridComponent, |
| DataZoomComponent |
| } from 'echarts/components'; |
| import VChart from 'vue-echarts'; |
| |
| use([ |
| CanvasRenderer, |
| BarChart, |
| LineChart, |
| TitleComponent, |
| TooltipComponent, |
| LegendComponent, |
| GridComponent, |
| DataZoomComponent |
| ]); |
| |
| const props = defineProps({ |
| data: { type: Array, required: true }, |
| benchmark: { type: String, default: 'MTBench' }, |
| metric: { type: String, default: 'throughput' } |
| }); |
| |
| const chartOption = computed(() => { |
| const isAllBenchmarks = props.benchmark === 'all'; |
| const benchmarksList = isAllBenchmarks ? ['gsm8k', 'math500', 'mtbench', 'humaneval', 'livecodebench', 'financeqa', 'gpqa'] : [props.benchmark]; |
| const metricKey = props.metric === 'accLen' ? 'accLen' : 'throughput'; |
| const metricLabel = props.metric === 'accLen' ? 'Acceptance Length' : 'Throughput (tokens/s)'; |
| const isSpeedup = props.metric === 'speedup'; |
| |
| |
| const validData = props.data.filter((d) => { |
| return benchmarksList.some(b => d.metrics[b] && d.metrics[b][metricKey] != null); |
| }); |
| |
| |
| const parallelConfigs = [...new Set(validData.map(d => d.parallelConfig).filter(c => c && c !== '-' && c !== null))]; |
| const parallelConfigText = parallelConfigs.length > 0 ? ` (${parallelConfigs.join(', ')})` : ''; |
| |
| |
| const series = []; |
| let xAxisData = []; |
| |
| |
| const getColor = (draftModel) => { |
| const draft = draftModel ? draftModel.toLowerCase() : ''; |
| if (draft.includes('specbundle')) { |
| return { |
| type: 'linear', |
| x: 0, y: 0, x2: 0, y2: 1, |
| colorStops: [{ offset: 0, color: '#4f46e5' }, { offset: 1, color: '#4338ca' }] |
| }; |
| } else if (draft.includes('eagle')) { |
| return '#10b981'; |
| } else if (draft === '-' || draft === 'none' || draft.includes('baseline')) { |
| return '#94a3b8'; |
| } |
| return '#f59e0b'; |
| }; |
| |
| const getLabel = (d) => { |
| const isBaseline = d.draftModel === '-' || d.draftModel === 'None' || d.draftModel.toLowerCase().includes('baseline'); |
| if (isBaseline) { |
| return 'Baseline (No Draft Model)'; |
| } |
| const cleanedModel = removeSGLangPrefix(d.draftModel); |
| const modelName = cleanedModel.split('/').pop(); |
| const config = (d.config === '0-0-0' || !d.config || d.config === '-') ? '' : d.config; |
| return config ? `${modelName}\n(${config})` : modelName; |
| }; |
| |
| if (isAllBenchmarks) { |
| |
| xAxisData = benchmarksList; |
| |
| |
| const palette = [ |
| '#06b6d4', |
| '#8b5cf6', |
| '#ec4899', |
| '#f59e0b', |
| '#14b8a6', |
| '#f43f5e', |
| '#3b82f6', |
| '#84cc16' |
| ]; |
| let colorIndex = 0; |
| |
| validData.forEach((d) => { |
| const seriesName = getLabel(d).replace(/\n/g, ' '); |
| |
| |
| const draft = d.draftModel ? d.draftModel.toLowerCase() : ''; |
| const isBaseline = draft === '-' || draft === 'none' || draft.includes('baseline'); |
| |
| let itemStyleColor; |
| |
| if (draft.includes('specbundle')) { |
| itemStyleColor = { |
| type: 'linear', |
| x: 0, y: 0, x2: 0, y2: 1, |
| colorStops: [{ offset: 0, color: '#4f46e5' }, { offset: 1, color: '#4338ca' }] |
| }; |
| } else if (isBaseline) { |
| itemStyleColor = '#94a3b8'; |
| } else { |
| itemStyleColor = palette[colorIndex % palette.length]; |
| colorIndex++; |
| } |
| |
| const data = benchmarksList.map(b => { |
| const val = d.metrics[b]?.[metricKey]; |
| if (val == null) return 0; |
| if (isSpeedup) { |
| const baseline = d.baseline && d.baseline[b] ? d.baseline[b][metricKey] : null; |
| return baseline ? parseFloat((val / baseline).toFixed(2)) : 0; |
| } |
| return typeof val === 'number' ? parseFloat(val.toFixed(2)) : val; |
| }); |
| |
| series.push({ |
| name: seriesName, |
| type: 'bar', |
| data: data, |
| itemStyle: { |
| borderRadius: [4, 4, 0, 0], |
| color: itemStyleColor |
| }, |
| label: { |
| show: true, |
| position: 'top', |
| fontSize: 9, |
| formatter: (params) => { |
| const val = params.value; |
| return typeof val === 'number' ? val.toFixed(2) : val; |
| }, |
| distance: 2 |
| } |
| }); |
| }); |
| |
| } else { |
| |
| xAxisData = validData.map(getLabel); |
| |
| const values = validData.map((d) => { |
| const val = d.metrics[benchmarksList[0]][metricKey]; |
| if (isSpeedup) { |
| const baseline = d.baseline && d.baseline[benchmarksList[0]] ? d.baseline[benchmarksList[0]][metricKey] : null; |
| return baseline ? parseFloat((val / baseline).toFixed(2)) : 0; |
| } |
| return typeof val === 'number' ? parseFloat(val.toFixed(2)) : val; |
| }); |
| |
| series.push({ |
| name: isSpeedup ? 'Speedup' : metricLabel, |
| type: 'bar', |
| data: values, |
| barMaxWidth: 60, |
| itemStyle: { |
| borderRadius: [6, 6, 0, 0], |
| color: function (params) { |
| const dataIndex = params.dataIndex; |
| return getColor(validData[dataIndex].draftModel); |
| } |
| }, |
| label: { |
| show: xAxisData.length <= 15, |
| position: 'top', |
| formatter: (params) => { |
| const val = params.value; |
| return typeof val === 'number' ? val.toFixed(2) : val; |
| }, |
| fontSize: 10, |
| color: '#64748b' |
| } |
| }); |
| } |
| |
| const displayTitle = isAllBenchmarks |
| ? `Hardware: H200 ${parallelConfigText} | Metric: Throughput (tokens/s)` |
| : `${benchmarksList[0]} Performance`; |
| |
| |
| return { |
| textStyle: { fontFamily: 'Inter, sans-serif' }, |
| title: { |
| text: displayTitle, |
| left: 'left', |
| textStyle: { |
| fontSize: 16, |
| fontWeight: 600, |
| color: '#1e293b' |
| } |
| }, |
| tooltip: { |
| trigger: 'axis', |
| backgroundColor: 'rgba(255, 255, 255, 0.95)', |
| borderColor: '#e2e8f0', |
| textStyle: { color: '#334155', fontSize: 12 }, |
| axisPointer: { type: 'shadow' } |
| }, |
| legend: { |
| show: isAllBenchmarks, |
| bottom: 0, |
| left: 'center', |
| width: '90%', |
| itemGap: 15, |
| textStyle: { |
| fontSize: 11 |
| }, |
| data: isAllBenchmarks ? series.map(s => s.name) : [] |
| }, |
| grid: { |
| left: 0, |
| right: 10, |
| bottom: 100, |
| top: 60, |
| containLabel: true |
| }, |
| xAxis: { |
| type: 'category', |
| data: xAxisData, |
| axisLine: { lineStyle: { color: '#e2e8f0' } }, |
| axisTick: { show: false }, |
| axisLabel: { |
| interval: 0, |
| rotate: isAllBenchmarks ? 0 : 30, |
| fontSize: 10, |
| color: '#64748b', |
| |
| } |
| }, |
| yAxis: { |
| type: 'value', |
| name: isSpeedup ? 'x Baseline' : '', |
| nameTextStyle: { align: 'right', color: '#94a3b8' }, |
| splitLine: { lineStyle: { type: 'dashed', color: '#f1f5f9' } }, |
| axisLabel: { color: '#64748b', fontSize: 11 } |
| }, |
| dataZoom: [ |
| { |
| type: 'slider', |
| show: !isAllBenchmarks && xAxisData.length > 8, |
| start: 0, |
| end: isAllBenchmarks ? 100 : Math.min(100, (8 / xAxisData.length) * 100), |
| bottom: 0, |
| height: 16, |
| borderColor: 'transparent', |
| backgroundColor: '#f8fafc', |
| fillerColor: 'rgba(79, 70, 229, 0.1)', |
| handleSize: '100%', |
| handleStyle: { color: '#818cf8' } |
| }, |
| { |
| type: 'inside', |
| start: 0, |
| end: 100, |
| zoomOnMouseWheel: false, |
| moveOnMouseMove: true, |
| moveOnMouseWheel: true |
| } |
| ], |
| series: series |
| }; |
| }); |
| </script> |
|
|
| <style scoped> |
| .chart-wrapper { |
| width: 100%; |
| height: 100%; |
| } |
| </style> |
|
|