* Copyright (c) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------------------------------------------*/
import Button from '@material-ui/core/Button';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import CardHeader from '@material-ui/core/CardHeader';
import Grid from '@material-ui/core/Grid';
import { makeStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import ChevronLeftIcon from '@material-ui/icons/ChevronLeft';
import { Select, Table } from 'antd';
import * as React from 'react';
import * as api from '../api';
import { useResizeEventDependency } from '../utils/resize';
import { FullCircularProgress } from './FullCircularProgress';
import * as echarts from 'echarts';
const { Option } = Select;
const topGraphHeight = 230;
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
},
pre: {
'& ul': {
margin: 0,
paddingLeft: theme.spacing(3),
...theme.typography.body1,
},
'& li': {},
'& a': {
color: '#ffa726',
},
'& a:active': {
color: '#ffa726',
},
'& p': {
margin: 0,
...theme.typography.subtitle1,
fontWeight: theme.typography.fontWeightBold,
},
},
topGraph: {
height: topGraphHeight + 40,
},
iconButton: {
padding: '8px',
},
}));
const getAngleByDataLength = (data: number): number => {
if (data < 10) {
return 0;
} else {
return 90 * (1 - (10 / data));
}
};
export interface DiffColumnChartIProps {
rawData: any[];
selectCallback: (row: number, column: number) => void;
}
export interface DiffStepChartIProps {
rawData: any[];
}
const DiffColumnChart: React.FC<DiffColumnChartIProps> = (props: DiffColumnChartIProps) => {
const { rawData, selectCallback } = props;
const graphRef = React.useRef<HTMLDivElement>(null);
const [resizeEventDependency] = useResizeEventDependency();
React.useLayoutEffect(() => {
const element = graphRef.current;
if (!element) {
return undefined;
}
const chart = echarts.init(element);
const options: echarts.EChartsOption = {
title: {
text: 'Execution Comparsion',
},
legend: {
top: 10,
right: 10,
},
tooltip: {
trigger: 'axis',
formatter: function (params: any) {
const index = params[0].name.indexOf('@');
const safeName = params[0].name.replace(/</g, '<').replace(/>/g, '>');
let res = `<b>${index > -1 ? safeName.slice(index + 1) : safeName}<b/> <br/>`;
for (const item of params) {
if (typeof item.value[item.encode.y[0]] === 'number') {
res += `<span style="background: ${item.color};
height:10px;
width: 10px;
border-radius: 50%;
display: inline-block;
margin-right:10px;">
</span>
${item.seriesName}: ${item.value[item.encode.y[0]]}<br/>`;
}
}
return res;
},
},
series: [
{
type: 'bar',
itemStyle: {
color: '#3366cc',
},
yAxisIndex: 0,
},
{
type: 'bar',
itemStyle: {
color: '#dc3912',
},
yAxisIndex: 0,
},
{
type: 'line',
itemStyle: {
color: '#ff9900',
},
yAxisIndex: 1,
},
{
type: 'line',
itemStyle: {
color: '#109618',
},
yAxisIndex: 1,
},
],
xAxis: {
type: 'category',
axisLabel: {
interval: 0,
rotate: getAngleByDataLength(rawData.length),
formatter: (name: string) => {
const index = name.indexOf('@');
const displayName = index > -1 ? name.slice(index + 1) : name;
return displayName.length > 16 ? `${displayName.slice(0, 14)}...` : displayName;
},
},
},
yAxis: [
{
type: 'value',
name: 'Time Difference(us)',
scale: true,
},
{
type: 'value',
name: 'Accumulated Difference(us)',
scale: true,
},
],
dataset: {
source: rawData.map((item, idx) => {
let param: any[] = [...item];
param[0] = `${idx}@${param[0]}`;
return param;
}),
},
};
if (options) {
chart.setOption(options, true);
}
chart.on('click', (param) => {
if (param.seriesIndex !== undefined) {
selectCallback(param.dataIndex, param.seriesIndex + 1);
}
});
return () => {
chart.dispose();
};
}, [rawData, resizeEventDependency]);
return (
<div>
<div ref={graphRef} style={{ height: '400px' }}></div>
</div>
);
};
const DiffStepChart: React.FC<DiffStepChartIProps> = (props: DiffStepChartIProps) => {
const { rawData } = props;
const graphRef = React.useRef<HTMLDivElement>(null);
const [resizeEventDependency] = useResizeEventDependency();
React.useLayoutEffect(() => {
const element = graphRef.current;
if (!element) {
return undefined;
}
const chart = echarts.init(element);
const options: echarts.EChartsOption = {
title: {
text: 'Execution Diff',
},
legend: {
top: 10,
right: 10,
},
dataset: {
source: rawData.map((item, idx) => {
let param: any[] = [...item];
param[0] = `${idx}@${param[0]}`;
return param;
}),
},
xAxis: {
type: 'category',
axisLabel: {
interval: 0,
rotate: getAngleByDataLength(rawData.length),
formatter: (name: string) => {
const index = name.indexOf('@');
const displayName = index > -1 ? name.slice(index + 1) : name;
return displayName.length > 16 ? `${displayName.slice(0, 14)}...` : displayName;
},
},
},
yAxis: {
type: 'value',
scale: true,
},
tooltip: {
trigger: 'axis',
formatter: function (params: any) {
const index = params[0].name.indexOf('@');
const safeName = params[0].name.replace(/</g, '<').replace(/>/g, '>');
let res = `<b>${index > -1 ? safeName.slice(index + 1) : safeName}<b/> <br/>`;
for (const item of params) {
if (typeof item.value[item.encode.y[0]] === 'number') {
res += `<span style="background: ${item.color};
height:10px;
width: 10px;
border-radius: 50%;
display: inline-block;
margin-right:10px;">
</span>
${item.seriesName}: ${item.value[item.encode.y[0]]}<br/>`;
}
}
return res;
},
},
series: [
{
type: 'line',
color: '#3366cc',
symbolSize: 0,
step: 'middle',
areaStyle: {
color: '#c1d1ef',
opacity: 1,
},
},
{
type: 'line',
color: '#dc3912',
symbolSize: 0,
step: 'middle',
areaStyle: {
color: '#f4c3b7',
opacity: 1,
},
},
],
};
if (options) {
chart.setOption(options, true);
}
return () => {
chart.dispose();
};
}, [rawData, resizeEventDependency]);
return (
<div>
<div ref={graphRef} style={{ height: 500 }}></div>
</div>
);
};
export interface IProps {
run: string;
worker: string;
span: string;
expRun: string;
expWorker: string;
expSpan: string;
}
export interface ColumnUnderlyingData {
name: string;
path: string;
leftAggs: any[];
rightAggs: any[];
}
export interface TableRow {
key: number;
operator: string;
baselineCalls?: number;
expCalls?: number;
deltaCalls?: number;
deltaCallsPercentNumber?: number;
deltaCallsPercent?: string;
baselineHostDuration: number;
expHostDuration: number;
deltaHostDuration: number;
deltaHostDurationPercentNumber: number;
deltaHostDurationPercent: string;
baselineSelfHostDuration: number;
expSelfHostDuration: number;
deltaSelfHostDuration: number;
deltaSelfHostDurationPercentNumber: number;
deltaSelfHostDurationPercent: string;
baselineDeviceDuration: number;
expDeviceDuration: number;
deltaDeviceDuration: number;
deltaDeviceDurationPercentNumber: number;
deltaDeviceDurationPercent: string;
baselineSelfDeviceDuration: number;
expSelfDeviceDuration: number;
deltaSelfDeviceDuration: number;
deltaSelfDeviceDurationPercentNumber: number;
deltaSelfDeviceDurationPercent: string;
}
let columnChartDataStack: any[][] = [];
let stepChartDataStack: any[][] = [];
let columnUnderlyingDataStack: ColumnUnderlyingData[][] = [];
let columnTableDataSourceStack: TableRow[][] = [];
export const DiffOverview: React.FC<IProps> = (props: IProps) => {
const COMPOSITE_NODES_NAME = 'CompositeNodes';
const hostDurationColumns = [
{
title: 'Baseline Host Duration (us)',
dataIndex: 'baselineHostDuration',
key: 'baselineHostDuration',
sorter: (a: TableRow, b: TableRow): number => {
const aBaselineHost = a.baselineHostDuration ?? 0;
const bBaselineHost = b.baselineHostDuration ?? 0;
return aBaselineHost - bBaselineHost;
},
},
{
title: 'Exp Host Duration (us)',
dataIndex: 'expHostDuration',
key: 'expHostDuration',
sorter: (a: TableRow, b: TableRow): number => {
const aExpHost = a.expHostDuration ?? 0;
const bExpHost = b.expHostDuration ?? 0;
return aExpHost - bExpHost;
},
},
{
title: 'Delta Host Duration (us)',
dataIndex: 'deltaHostDuration',
key: 'deltaHostDuration',
sorter: (a: TableRow, b: TableRow): number => {
const aDeltaHost = a.deltaHostDuration ?? 0;
const bDeltaHost = b.deltaHostDuration ?? 0;
return aDeltaHost - bDeltaHost;
},
},
{
title: 'Delta Host Duration%',
dataIndex: 'deltaHostDurationPercent',
key: 'deltaHostDurationPercent',
sorter: (a: TableRow, b: TableRow): number => {
const aPercent = a.deltaHostDurationPercentNumber ?? 0;
const bPercent = b.deltaHostDurationPercentNumber ?? 0;
return aPercent - bPercent;
},
},
];
const selfHostDurationColumns = [
{
title: 'Baseline Self Host Duration (us)',
dataIndex: 'baselineSelfHostDuration',
key: 'baselineSelfHostDuration',
sorter: (a: TableRow, b: TableRow): number => a.baselineSelfHostDuration - b.baselineSelfHostDuration,
},
{
title: 'Exp Self Host Duration (us)',
dataIndex: 'expSelfHostDuration',
key: 'expSelfHostDuration',
sorter: (a: TableRow, b: TableRow): number => a.expSelfHostDuration - b.expSelfHostDuration,
},
{
title: 'Delta Self Host Duration (us)',
dataIndex: 'deltaSelfHostDuration',
key: 'deltaSelfHostDuration',
sorter: (a: TableRow, b: TableRow): number => {
const aDeltaSelfHost = a.deltaSelfHostDuration ?? 0;
const bDeltaSelfHost = b.deltaSelfHostDuration ?? 0;
return aDeltaSelfHost - bDeltaSelfHost;
},
},
{
title: 'Delta Self Host Duration%',
dataIndex: 'deltaSelfHostDurationPercent',
key: 'deltaSelfHostDurationPercent',
sorter: (a: TableRow, b: TableRow): number => {
const aSelfPercent = a.deltaSelfHostDurationPercentNumber ?? 0;
const bSelfPercent = b.deltaSelfHostDurationPercentNumber ?? 0;
return aSelfPercent - bSelfPercent;
},
},
];
const deviceDurationColumns = [
{
title: 'Baseline Device Duration (us)',
dataIndex: 'baselineDeviceDuration',
key: 'baselineDeviceDuration',
sorter: (a: TableRow, b: TableRow): number => a.baselineDeviceDuration - b.baselineDeviceDuration,
},
{
title: 'Exp Device Duration (us)',
dataIndex: 'expDeviceDuration',
key: 'expDeviceDuration',
sorter: (a: TableRow, b: TableRow): number => a.expDeviceDuration - b.expDeviceDuration,
},
{
title: 'Delta Device Duration (us)',
dataIndex: 'deltaDeviceDuration',
key: 'deltaDeviceDuration',
sorter: (a: TableRow, b: TableRow): number => {
const aDeltaDeviceDuration = a.deltaDeviceDuration ?? 0;
const bdeltaDeviceDuration = b.deltaDeviceDuration ?? 0;
return aDeltaDeviceDuration - bdeltaDeviceDuration;
},
},
{
title: 'Delta Device Duration%',
dataIndex: 'deltaDeviceDurationPercent',
key: 'deltaDeviceDurationPercent',
sorter: (a: TableRow, b: TableRow): number => {
const aDeltaDeviceDurationPercentNumber = a.deltaDeviceDurationPercentNumber ?? 0;
const bDeltaDeviceDurationPercentNumber = b.deltaDeviceDurationPercentNumber ?? 0;
return aDeltaDeviceDurationPercentNumber - bDeltaDeviceDurationPercentNumber;
},
},
];
const selfDeviceDurationColumns = [
{
title: 'Baseline Self Device Duration (us)',
dataIndex: 'baselineSelfDeviceDuration',
key: 'baselineSelfDeviceDuration',
sorter: (a: TableRow, b: TableRow): number => a.baselineSelfDeviceDuration - b.baselineSelfDeviceDuration,
},
{
title: 'Exp Self Device Duration (us)',
dataIndex: 'expSelfDeviceDuration',
key: 'expSelfDeviceDuration',
sorter: (a: TableRow, b: TableRow): number => a.expSelfDeviceDuration - b.expSelfDeviceDuration,
},
{
title: 'Delta Self Device Duration (us)',
dataIndex: 'deltaSelfDeviceDuration',
key: 'deltaSelfDeviceDuration',
sorter: (a: TableRow, b: TableRow): number => {
const aDeltaSelfDeviceDuration = a.deltaSelfDeviceDuration ?? 0;
const bDeltaSelfDeviceDuration = b.deltaSelfDeviceDuration ?? 0;
return aDeltaSelfDeviceDuration - bDeltaSelfDeviceDuration;
},
},
{
title: 'Delta Self Device Duration%',
dataIndex: 'deltaSelfDeviceDurationPercent',
key: 'deltaSelfDeviceDurationPercent',
sorter: (a: TableRow, b: TableRow): number => {
const aDeltaSelfDeviceDurationPercentNumber = a.deltaSelfDeviceDurationPercentNumber ?? 0;
const bDeltaSelfDeviceDurationPercentNumber = b.deltaSelfDeviceDurationPercentNumber ?? 0;
return aDeltaSelfDeviceDurationPercentNumber - bDeltaSelfDeviceDurationPercentNumber;
},
},
];
interface IColumnMap {
[key: string]: any;
}
type IColumnMapType = IColumnMap;
const tableSourceColumnMap: IColumnMapType = {
selfHostDuration: selfHostDurationColumns,
hostDuration: hostDurationColumns,
deviceDuration: deviceDurationColumns,
selfDeviceDuration: selfDeviceDurationColumns,
};
const baseTableColumns = [
{
title: 'Operator',
dataIndex: 'operator',
key: 'operator',
sorter: (a: TableRow, b: TableRow) => a.operator.localeCompare(b.operator),
},
{
title: 'Baseline Calls',
dataIndex: 'baselineCalls',
key: 'baselineCalls',
sorter: (a: TableRow, b: TableRow) => a.baselineCalls ?? 0 - (b.baselineCalls ?? 0),
},
{
title: 'Exp Calls',
dataIndex: 'expCalls',
key: 'expCalls',
sorter: (a: TableRow, b: TableRow) => a.expCalls ?? 0 - (b.expCalls ?? 0),
},
{
title: 'Delta Calls',
dataIndex: 'deltaCalls',
key: 'deltaCalls',
sorter: (a: TableRow, b: TableRow) => a.deltaCalls ?? 0 - (b.deltaCalls ?? 0),
},
{
title: 'Delta Calls%',
dataIndex: 'deltaCallsPercent',
key: 'deltaCallsPercent',
sorter: (a: TableRow, b: TableRow) => a.deltaCallsPercentNumber ?? 0 - (b.deltaCallsPercentNumber ?? 0),
},
];
const [tableDataSource, setTableDataSource] = React.useState<TableRow[]>([]);
const { run, worker, span, expRun, expWorker, expSpan } = props;
const [columnUnderlyingData, setColumnUnderlyingData] = React.useState<ColumnUnderlyingData[]>([]);
const [rootUnderlyingData, setRootUnderlyingData] = React.useState<ColumnUnderlyingData>();
const [columnChartData, setColumnChartData] = React.useState<any[]>([]);
const [stepChartData, setStepChartData] = React.useState<any[]>([]);
const [selectedTableColumnsOptions, setSelectedTableColumnsOptions] = React.useState<[key: string]>(['hostDuration']);
const [selectedTableColumns, setSelectedTableColumns] = React.useState<any[]>([
...baseTableColumns,
...hostDurationColumns,
]);
const [dataStackLevel, setDataStackLevel] = React.useState(0);
const [loading, setLoading] = React.useState(false);
const classes = useStyles();
const handleChartColumnSelect = (row: number, column: number): void => {
if (columnUnderlyingData.length === 0) {
return;
}
let selectedUnderlyingData = columnUnderlyingData[row];
if (!selectedUnderlyingData) {
return;
}
let tableDataSource1 = generateDataSourceFromUnderlyingData(selectedUnderlyingData);
setTableDataSource(tableDataSource1);
columnTableDataSourceStack.push(tableDataSource1);
setLoading(true);
api.defaultApi
.diffnodeGet(run, worker, span, expRun, expWorker, expSpan, selectedUnderlyingData.path)
.then((resp) => handleDiffNodeResp(resp))
.finally(() => setLoading(false));
};
const handleGoBack = (): void => {
if (columnChartDataStack.length > 1) {
columnChartDataStack.pop();
let top = columnChartDataStack[columnChartDataStack.length - 1];
setColumnChartData(top);
}
if (stepChartDataStack.length > 1) {
stepChartDataStack.pop();
let top = stepChartDataStack[stepChartDataStack.length - 1];
setStepChartData(top);
}
if (columnUnderlyingDataStack.length > 0) {
columnUnderlyingDataStack.pop();
let top = columnUnderlyingDataStack[columnUnderlyingDataStack.length - 1];
setColumnUnderlyingData(top);
}
if (columnTableDataSourceStack.length > 0) {
columnTableDataSourceStack.pop();
let top = columnTableDataSourceStack[columnTableDataSourceStack.length - 1];
if (top) {
setTableDataSource(top);
} else {
let tableDataSource2 = generateDataSourceFromUnderlyingData(rootUnderlyingData);
setTableDataSource(tableDataSource2);
}
}
setDataStackLevel(dataStackLevel - 1);
};
const toPercentString = (percentNumber: number): string => {
if (isNaN(percentNumber)) {
return 'N/A';
}
return `${percentNumber.toFixed(2)}%`;
};
const handleColumnSelectionChange = (value: [key: string]): void => {
let columns = value.map((x) => tableSourceColumnMap[x]).flat();
let r = [...baseTableColumns, ...columns];
setSelectedTableColumnsOptions(value);
setSelectedTableColumns(r);
};
const generateDataSourceFromUnderlyingData = (selectedUnderlyingData?: ColumnUnderlyingData): TableRow[] => {
if (!selectedUnderlyingData) {
return [];
}
let newTableDataSource: TableRow[] = [];
for (let i = 0; i < selectedUnderlyingData.leftAggs.length; i++) {
let left = selectedUnderlyingData.leftAggs[i];
let right = selectedUnderlyingData.rightAggs[i];
let deltaCallsPercentNumber = ((right.calls - left.calls) / left.calls) * 100;
let deltaHostDurationPercentNumber = ((right.host_duration - left.host_duration) / left.host_duration) * 100;
let deltaSelfHostDurationPercentNumber =
((right.self_host_duration - left.self_host_duration) / left.self_host_duration) * 100;
let deltaDeviceDurationPercentNumber =
((right.device_duration - left.device_duration) / left.device_duration) * 100;
let deltaSelfDeviceDurationPercentNumber =
((right.self_device_duration - left.self_device_duration) / left.self_device_duration) * 100;
newTableDataSource.push({
key: i,
operator: left.name,
baselineCalls: left.calls,
expCalls: right.calls,
deltaCalls: right.calls - left.calls,
deltaCallsPercentNumber: deltaCallsPercentNumber,
deltaCallsPercent: toPercentString(deltaCallsPercentNumber),
baselineHostDuration: left.host_duration,
expHostDuration: right.host_duration,
deltaHostDuration: parseFloat((right.host_duration - left.host_duration).toFixed(3)),
deltaHostDurationPercentNumber: deltaHostDurationPercentNumber,
deltaHostDurationPercent: toPercentString(deltaHostDurationPercentNumber),
baselineSelfHostDuration: left.self_host_duration,
expSelfHostDuration: right.self_host_duration,
deltaSelfHostDuration: parseFloat((right.self_host_duration - left.self_host_duration).toFixed(3)),
deltaSelfHostDurationPercentNumber: deltaSelfHostDurationPercentNumber,
deltaSelfHostDurationPercent: toPercentString(deltaSelfHostDurationPercentNumber),
baselineDeviceDuration: left.device_duration,
expDeviceDuration: right.device_duration,
deltaDeviceDuration: parseFloat((right.device_duration - left.device_duration).toFixed(3)),
deltaDeviceDurationPercentNumber: deltaDeviceDurationPercentNumber,
deltaDeviceDurationPercent: toPercentString(deltaDeviceDurationPercentNumber),
baselineSelfDeviceDuration: left.self_device_duration,
expSelfDeviceDuration: right.self_device_duration,
deltaSelfDeviceDuration: parseFloat((right.self_device_duration - left.self_device_duration).toFixed(3)),
deltaSelfDeviceDurationPercentNumber: deltaSelfDeviceDurationPercentNumber,
deltaSelfDeviceDurationPercent: toPercentString(deltaSelfDeviceDurationPercentNumber),
});
}
return newTableDataSource;
};
React.useEffect(() => {
const hasData =
run.length > 0 &&
worker.length > 0 &&
span.length > 0 &&
expRun.length > 0 &&
expWorker.length > 0 &&
expSpan.length > 0;
if (hasData) {
setLoading(true);
columnChartDataStack = [];
stepChartDataStack = [];
columnUnderlyingDataStack = [];
columnTableDataSourceStack = [];
api.defaultApi
.diffnodeGet(run, worker, span, expRun, expWorker, expSpan)
.then((resp) => {
handleDiffNodeResp(resp);
let newRootUnderlyingData = {
name: 'rootNode',
path: resp.path,
leftAggs: resp.left.aggs,
rightAggs: resp.right.aggs,
};
setRootUnderlyingData(newRootUnderlyingData);
let tableDataSource3 = generateDataSourceFromUnderlyingData(newRootUnderlyingData);
setTableDataSource(tableDataSource3);
})
.finally(() => setLoading(false));
setSelectedTableColumns([...baseTableColumns, ...hostDurationColumns]);
}
}, [run, worker, span, expRun, expWorker, expSpan]);
const handleDiffNodeResp = (resp: any): void => {
let newColumnChartData: any[] = [];
let newStepChartData: any[] = [];
let underlyingData: ColumnUnderlyingData[] = [];
newColumnChartData.push(['Call', 'Baseline', 'Experiment', 'Baseline Trend', 'Exp Trend']);
newStepChartData.push(['Call', 'Diff', 'Accumulated Diff']);
if (resp.children.length > 0) {
let accumulatedLeftDuration = 0;
let accumulatedRightDuration = 0;
let accumulatedStepDiff = 0;
for (let i = 0; i < resp.children.length; i++) {
let left = resp.children[i].left;
let right = resp.children[i].right;
let currColumn: any[] = [];
let currStep: any[] = [];
let name = left.name;
if (name === COMPOSITE_NODES_NAME) {
continue;
}
if (name.startsWith('aten::')) {
continue;
}
if (name.startsWith('enumerate(DataLoader)')) {
name = name.substring(21);
}
if (name.startsWith('enumerate(DataPipe)')) {
name = name.substring(19);
}
if (name.startsWith('nn.Module: ')) {
name = name.substring(11);
}
if (name.startsWith('Optimizer.zero_grad')) {
name = 'Optimizer.zero_grad';
}
if (name.startsWith('Optimizer.step')) {
name = 'Optimizer.step';
}
currColumn.push(name);
currColumn.push(left.total_duration);
currColumn.push(right.total_duration);
accumulatedLeftDuration += left.total_duration;
currColumn.push(accumulatedLeftDuration);
accumulatedRightDuration += right.total_duration;
currColumn.push(accumulatedRightDuration);
newColumnChartData.push(currColumn);
underlyingData.push({
name: name,
path: resp.children[i].path,
leftAggs: left.aggs,
rightAggs: right.aggs,
});
currStep.push(name);
let stepDiff = right.total_duration - left.total_duration;
currStep.push(stepDiff);
accumulatedStepDiff += stepDiff;
currStep.push(accumulatedStepDiff);
newStepChartData.push(currStep);
}
} else {
let left = resp.left;
let right = resp.right;
let currColumn: any[] = [];
let currStep: any[] = [];
let name = left.name;
if (name.startsWith('nn.Module: ')) {
name = name.substring(11);
}
currColumn.push(name);
currColumn.push(left.total_duration);
currColumn.push(right.total_duration);
currColumn.push(left.total_duration);
currColumn.push(right.total_duration);
newColumnChartData.push(currColumn);
currStep.push(name);
let stepDiff = right.total_duration - left.total_duration;
currStep.push(stepDiff);
currStep.push(stepDiff);
newStepChartData.push(currStep);
}
setColumnChartData(newColumnChartData);
columnChartDataStack.push(newColumnChartData);
setStepChartData(newStepChartData);
stepChartDataStack.push(newStepChartData);
setColumnUnderlyingData(underlyingData);
columnUnderlyingDataStack.push(underlyingData);
setDataStackLevel(columnChartDataStack.length);
};
if (!loading && columnUnderlyingDataStack.length === 0) {
return (
<Card variant='outlined'>
<CardHeader title='No Runs Found'></CardHeader>
<CardContent>
<Typography>There is no run selected for diff.</Typography>
</CardContent>
</Card>
);
}
if (loading) {
return <FullCircularProgress />;
}
return (
<div className={classes.root}>
<Grid container spacing={1}>
<Grid container item spacing={1}>
<Grid item sm={12}>
<Card variant='outlined'>
<CardHeader title='DiffView' />
<CardContent>
<Button
className={classes.iconButton}
startIcon={<ChevronLeftIcon />}
onClick={handleGoBack}
variant='outlined'
disabled={dataStackLevel < 2}
>
Go Back
</Button>
{columnChartData.length > 1 && (
<>
<DiffColumnChart rawData={columnChartData} selectCallback={handleChartColumnSelect} />
<DiffStepChart rawData={stepChartData} />
</>
)}
{columnChartData.length === 1 && <Typography>No more level to show.</Typography>}
</CardContent>
</Card>
</Grid>
</Grid>
<Grid container item spacing={1}>
<Grid item sm={12}>
<Card variant='outlined'>
<CardHeader title='Operator View' />
<CardContent>
<Select
mode='multiple'
style={{ width: '100%' }}
placeholder='Select the data you need'
value={selectedTableColumnsOptions}
onChange={handleColumnSelectionChange}
optionLabelProp='label'
>
<Option value='hostDuration' label='Host Duration'>
<div>Host Duration</div>
</Option>
<Option value='selfHostDuration' label='Self Host Duration'>
<div>Self Host Duration</div>
</Option>
<Option value='deviceDuration' label='Device Duration'>
<div>Device Duration</div>
</Option>
<Option value='selfDeviceDuration' label='Self Device Duration'>
<div>Self Device Duration</div>
</Option>
</Select>
<Table dataSource={tableDataSource} columns={selectedTableColumns} />
</CardContent>
</Card>
</Grid>
</Grid>
</Grid>
</div>
);
};