Browse Source

add profiling new page

tags/v0.5.0-beta
ph 5 years ago
parent
commit
d73c66bda2
6 changed files with 2832 additions and 0 deletions
  1. +31
    -0
      mindinsight/ui/src/assets/images/collapse-left.svg
  2. +31
    -0
      mindinsight/ui/src/assets/images/collapse-right.svg
  3. +1164
    -0
      mindinsight/ui/src/views/train-manage/operator.vue
  4. +738
    -0
      mindinsight/ui/src/views/train-manage/profiling-dashboard.vue
  5. +208
    -0
      mindinsight/ui/src/views/train-manage/profiling.vue
  6. +660
    -0
      mindinsight/ui/src/views/train-manage/step-trace.vue

+ 31
- 0
mindinsight/ui/src/assets/images/collapse-left.svg View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="32px" height="96px" viewBox="0 0 32 96" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 63.1 (92452) - https://sketch.com -->
<title>编组 12</title>
<desc>Created with Sketch.</desc>
<defs>
<filter x="-166.7%" y="-25.0%" width="433.3%" height="150.0%" filterUnits="objectBoundingBox" id="filter-1">
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<polygon id="path-2" points="0 0 8 0 4 5"></polygon>
</defs>
<g id="智能小助手" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="3" transform="translate(-461.000000, -525.000000)">
<g id="编组-12" filter="url(#filter-1)" transform="translate(471.000000, 533.000000)">
<path d="M-1.50990331e-14,0 L9.11107923,10.6295924 C10.9752941,12.8045097 12,15.5745537 12,18.4390889 L12,61.5609111 C12,64.4254463 10.9752941,67.1954903 9.11107923,69.3704076 L-1.50990331e-14,80 L-1.50990331e-14,80 L-1.50990331e-14,0 Z" id="矩形" fill="#FFFFFF"></path>
<g id="icon-下展" transform="translate(6.000000, 40.500000) rotate(90.000000) translate(-6.000000, -40.500000) translate(2.000000, 38.000000)">
<mask id="mask-3" fill="white">
<use xlink:href="#path-2"></use>
</mask>
<use id="蒙版" fill="#575D6C" fill-rule="evenodd" xlink:href="#path-2"></use>
</g>
</g>
</g>
</g>
</svg>

+ 31
- 0
mindinsight/ui/src/assets/images/collapse-right.svg View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="32px" height="96px" viewBox="0 0 32 96" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 63.1 (92452) - https://sketch.com -->
<title>编组 12</title>
<desc>Created with Sketch.</desc>
<defs>
<filter x="-166.7%" y="-25.0%" width="433.3%" height="150.0%" filterUnits="objectBoundingBox" id="filter-1">
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<polygon id="path-2" points="0 0 8 0 4 5"></polygon>
</defs>
<g id="智能小助手" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="3" transform="translate(-461.000000, -525.000000)">
<g id="编组-12" filter="url(#filter-1)" transform="translate(471.000000, 533.000000)">
<path d="M-1.50990331e-14,0 L9.11107923,10.6295924 C10.9752941,12.8045097 12,15.5745537 12,18.4390889 L12,61.5609111 C12,64.4254463 10.9752941,67.1954903 9.11107923,69.3704076 L-1.50990331e-14,80 L-1.50990331e-14,80 L-1.50990331e-14,0 Z" id="矩形" fill="#FFFFFF"></path>
<g id="icon-下展" transform="translate(6.000000, 40.500000) rotate(-90.000000) translate(-6.000000, -40.500000) translate(2.000000, 38.000000)">
<mask id="mask-3" fill="white">
<use xlink:href="#path-2"></use>
</mask>
<use id="蒙版" fill="#575D6C" fill-rule="evenodd" xlink:href="#path-2"></use>
</g>
</g>
</g>
</g>
</svg>

+ 1164
- 0
mindinsight/ui/src/views/train-manage/operator.vue
File diff suppressed because it is too large
View File


+ 738
- 0
mindinsight/ui/src/views/train-manage/profiling-dashboard.vue View File

@@ -0,0 +1,738 @@
<!--
Copyright 2020 Huawei Technologies Co., Ltd.All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<template>
<div class="pro-router-wrap">
<div class="pro-router-left">
<div class="step-trace">
<div class="title-wrap">
<div class="title">{{ $t('profiling.stepTrace') }}</div>
<div class="view-detail">
<button @click="viewDetail('step-trace')"
:disabled="svg.noData && svg.data.length === 0"
:class="{disabled:svg.noData && svg.data.length === 0}">{{ $t('profiling.viewDetail') }}
<i class="el-icon-d-arrow-right"></i></button>
</div>
<div class="tip-icon"
v-show="false">
<el-tooltip content=""
placement="top"
effect="light">
<i class="el-icon-info"></i>
</el-tooltip>
</div>
</div>
<div class="trace-container">
<div id="trace"
class="training-trace">
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg"
height="100%"
width="100%">
<defs>
<marker id="marker_end"
refX="5"
refY="4"
markerWidth="10"
markerHeight="8"
orient="auto">
<path d="M1,1 L1,7 L9,4 z"
fill="#E6EBF5"
stroke="#E6EBF5"></path>
</marker>
<marker id="marker_start"
refX="5"
refY="4"
markerWidth="10"
markerHeight="8"
orient="auto">
<path d="M9,1 L9,7 L1,4 z"
fill="#E6EBF5"
stroke="#E6EBF5"></path>
</marker>
</defs>
</svg>
</div>
<div class="image-noData"
v-if="svg.noData">
<div>
<img :src="require('@/assets/images/nodata.png')"
alt="" />
</div>
<p>{{$t("public.noData")}}</p>
</div>
</div>
</div>
<div class="minddata">
<div class="title-wrap">
<div class="title">{{ $t('profiling.mindData') }}</div>
<div class="view-detail"
v-if="false">
<button @click="viewDetail('minddata')">{{ $t('profiling.viewDetail') }}
<i class="el-icon-d-arrow-right"></i></button>
</div>
</div>
<div class="coming-soon-content">
<div class="coming-soon-container">
<img :src="require('@/assets/images/coming-soon.png')" />
<p class='coming-soon-text'>
{{$t("public.stayTuned")}}
</p>
</div>
</div>
</div>
</div>
<div class="pro-router-right">
<div class="op-time-consume">
<div class="title-wrap">
<div class="title">{{ $t('profiling.rankOfOperator') }}</div>
<div class="view-detail">
<button @click="viewDetail('operator')"
:disabled="pieChart.noData && pieChart.data.length === 0"
:class="{disabled:pieChart.noData && pieChart.data.length === 0}">{{ $t('profiling.viewDetail') }}
<i class="el-icon-d-arrow-right"></i></button>
</div>
</div>
<div class="image-noData"
v-if="pieChart.noData && pieChart.data.length === 0">
<div>
<img :src="require('@/assets/images/nodata.png')"
alt="" />
</div>
<p>{{$t("public.noData")}}</p>
</div>
<div class="op-time-content">
<div id="pieChart"
class="pie-chart"
v-if="pieChart.data.length"></div>
<div class="time-list"
v-if="pieChart.data.length">
<ul>
<li v-for="(item, index) in pieChart.topN"
:key="index"
class="item">
<span class="index"
:style="{'background-color': pieChart.colorList[index]}">{{index + 1}}</span>
<span class="name">{{item.name}}</span>
<span class="num">{{item.frequency + $t('profiling.times')}}</span>
<span class="time">
<span class="bar"
:style="{width: item.time / pieChart.topN[0].time * 100 + '%'}"></span>
<span class="value">{{item.time}}ms</span>
</span>
</li>
</ul>
</div>
</div>
</div>
<div class="time-line">
<div class="title-wrap">
<div class="title">{{ $t('profiling.timeLine') }}</div>
<div class="view-detail"
v-show="false">
<a @click="toPerfetto()">{{ $t('profiling.viewDetail') }} <i class="el-icon-d-arrow-right"></i></a>
</div>
</div>
<div class="coming-soon-content">
<div class="coming-soon-container">
<img :src="require('@/assets/images/coming-soon.png')" />
<p class='coming-soon-text'>
{{$t("public.stayTuned")}}
</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import echarts from 'echarts';
import RequestService from '../../services/request-service';
import CommonProperty from '../../common/common-property';
export default {
data() {
return {
svg: {
data: [],
svgPadding: 20,
totalWidth: 0,
totalTime: 0,
rowHeight: 60,
markerPadding: 4,
namespaceURI: 'http://www.w3.org/2000/svg',
resizeTimer: null,
colorList: [
['#A6DD82', '#edf8e6'],
['#6CBFFF', '#e2f2ff'],
['#fa8e5b', '#fff4de'],
['#01a5a7', '#cceded'],
],
colorIndex: 0,
noData: false,
},
trainingJobId: this.$route.query.id,
summaryPath: this.$route.query.dir,
relativePath: this.$route.query.path,
currentCard: '',
pieChart: {
chartDom: null,
data: [],
noData: false,
topN: [],
colorList: ['#6C92FA', '#6CBFFF', '#4EDED2', '#7ADFA0', '#A6DD82'],
},
};
},
mounted() {},
watch: {
'$parent.curDashboardInfo': {
handler(newValue, oldValue) {
if (newValue.query.dir && newValue.query.id && newValue.query.path) {
this.summaryPath = newValue.query.dir;
this.trainingJobId = newValue.query.id;
this.relativePath = newValue.query.path;
this.currentCard = newValue.curCardNum;
if (this.trainingJobId) {
document.title = `${decodeURIComponent(
this.trainingJobId,
)}-${this.$t('profiling.profilingDashboard')}
-MindInsight`;
} else {
document.title = `${this.$t(
'profiling.profilingDashboard',
)}-MindInsight`;
}
this.init();
}
},
deep: true,
immediate: true,
},
},
methods: {
init() {
this.queryTrainingTrace();
this.initPieChart();
window.addEventListener('resize', this.resizeTrace, false);
this.$bus.$on('resize', this.resizeTrace);
},
viewDetail(path) {
this.$router.push({
path,
query: {
id: this.trainingJobId,
dir: this.summaryPath,
path: this.relativePath,
},
});
},
setPieOption() {
const option = {};
option.tooltip = {
trigger: 'item',
formatter: (params) => {
return `${params.marker} ${params.data.name} ${params.percent}%`;
},
};
option.series = [
{
type: 'pie',
center: ['50%', '50%'],
data: this.pieChart.data,
radius: '50%',
lable: {
position: 'outer',
alignTo: 'none',
bleedMargin: 5,
},
itemStyle: {
normal: {
color: function(params) {
return CommonProperty.pieColorArr[params.dataIndex];
},
},
},
},
];
this.$nextTick(() => {
const dom = document.getElementById('pieChart');
if (dom) {
this.pieChart.chartDom = echarts.init(dom, null);
} else {
if (this.pieChart.chartDom) {
this.pieChart.chartDom.clear();
}
return;
}
this.pieChart.chartDom.setOption(option, true);
this.pieChart.chartDom.resize();
}, 10);
},
initPieChart() {
const params = {};
params.params = {
profile: this.summaryPath,
train_id: this.trainingJobId,
};
params.body = {
op_type: 'aicore_type',
device_id: this.currentCard,
filter_condition: {},
sort_condition: {
name: 'execution_time',
type: 'descending',
},
};
RequestService.getProfilerOpData(params)
.then((res) => {
if (res && res.data) {
if (res.data.object) {
this.pieChart.data = [];
res.data.object.forEach((item) => {
if (this.pieChart.data && this.pieChart.data.length < 19) {
this.pieChart.data.push({
name: item[0],
value: item[1],
frequency: item[2],
percent: item[3],
});
} else {
if (!this.pieChart.data[19]) {
this.pieChart.data[19] = {
name: 'Other',
value: 0,
percent: 0,
};
}
this.pieChart.data[19].value += item[1];
this.pieChart.data[19].percent += item[3];
}
});
this.setPieOption();
if (this.pieChart.data.length === 0) {
this.pieChart.noData = true;
}
this.pieChart.topN = this.pieChart.data
.slice(0, Math.min(this.pieChart.data.length, 5))
.map((i) => {
return {
name: i.name,
time: i.value,
frequency: i.frequency,
};
});
}
}
})
.catch(() => {
this.pieChart.noData = true;
});
},
queryTrainingTrace() {
const params = {
dir: this.relativePath,
type: 0,
device_id: this.currentCard,
};
RequestService.queryTrainingTrace(params).then(
(res) => {
if (
res.data &&
res.data.training_trace_graph &&
res.data.training_trace_graph.length
) {
this.svg.noData = false;
document.querySelector('#trace').style.height = `${res.data
.training_trace_graph.length * this.svg.rowHeight}px`;
this.svg.data = JSON.parse(
JSON.stringify(res.data.training_trace_graph),
);
this.removeTrace();
setTimeout(() => {
this.dealTraceData();
}, 100);
} else {
document.querySelector('#trace').style.height = '0px';
this.svg.noData = true;
this.svg.data = [];
this.removeTrace();
}
},
(error) => {
document.querySelector('#trace').style.height = '0px';
this.svg.noData = true;
this.svg.data = [];
this.removeTrace();
},
);
},

dealTraceData() {
const traceDom = document.querySelector('#trace');
if (traceDom) {
this.svg.totalWidth = traceDom.offsetWidth - this.svg.svgPadding * 2;
if (this.svg.data[0] && this.svg.data[0].length) {
const svg = document.querySelector('#trace svg');
this.svg.totalTime = this.svg.data[0][0].duration;
if (this.svg.totalTime) {
this.svg.data.forEach((row, index) => {
if (row && row.length) {
const dashedLine = this.addDashedLine(index);
svg.insertBefore(dashedLine, svg.querySelector('g'));
row.forEach((i) => {
if (i.duration) {
const tempDom = i.name
? this.createRect(i, index)
: this.createArrow(i, index);
svg.insertBefore(tempDom, svg.querySelector('g'));
}
});
}
});
}
} else {
this.removeTrace();
}
}
},
addDashedLine(index) {
const x1 = this.svg.svgPadding;
const x2 = this.svg.svgPadding + this.svg.totalWidth;
const y = index * this.svg.rowHeight;
const line = document.createElementNS(this.svg.namespaceURI, 'line');
line.setAttribute('x1', x1);
line.setAttribute('y1', y);
line.setAttribute('x2', x2);
line.setAttribute('y2', y);
line.setAttribute('style', 'stroke:#E2E2E2;stroke-width:1');
line.setAttribute('stroke-dasharray', '5 5');
const g = document.createElementNS(this.svg.namespaceURI, 'g');
g.appendChild(line);
return g;
},
createRect(data, rowIndex) {
const color = this.svg.colorList[this.svg.colorIndex++ % 4];
const height = 40;
const width = (data.duration / this.svg.totalTime) * this.svg.totalWidth;
const x1 =
(data.start / this.svg.totalTime) * this.svg.totalWidth +
this.svg.svgPadding;
const y1 =
rowIndex * this.svg.rowHeight + (this.svg.rowHeight - height) / 2;
const g = document.createElementNS(this.svg.namespaceURI, 'g');
const gChild = document.createElementNS(this.svg.namespaceURI, 'g');

const rect = document.createElementNS(this.svg.namespaceURI, 'rect');
rect.setAttribute('x', x1);
rect.setAttribute('y', y1);
rect.setAttribute('height', height);
rect.setAttribute('width', width);
rect.setAttribute('style', `fill:${color[1]};stroke:${color[1]};`);

const foreignObject = document.createElementNS(
this.svg.namespaceURI,
'foreignObject',
);
foreignObject.setAttribute('x', x1);
foreignObject.setAttribute('y', y1);
foreignObject.setAttribute('height', height);
foreignObject.setAttribute('width', width);
foreignObject.setAttribute(
'style',
`overflow:hidden;text-align:center;text-overflow:ellipsis;` +
`white-space:nowrap;font-size:12px;line-height:${height}px;color:${color[0]}`,
);
foreignObject.textContent = `${data.name}: ${data.duration.toFixed(4)}ms`;

const title = document.createElementNS(this.svg.namespaceURI, 'title');
title.textContent = `${data.name}: ${data.duration.toFixed(4)}ms`;

gChild.appendChild(rect);
gChild.appendChild(foreignObject);
gChild.appendChild(title);
g.appendChild(gChild);
return g;
},

createArrow(data, rowIndex) {
const width = (data.duration / this.svg.totalTime) * this.svg.totalWidth;
const x1 =
(data.start / this.svg.totalTime) * this.svg.totalWidth +
this.svg.markerPadding +
this.svg.svgPadding;
const x2 = x1 + width - this.svg.markerPadding * 2;
const y = rowIndex * this.svg.rowHeight + this.svg.rowHeight / 2;
const g = document.createElementNS(this.svg.namespaceURI, 'g');

const line = document.createElementNS(this.svg.namespaceURI, 'line');
line.setAttribute('x1', x1);
line.setAttribute('y1', y);
line.setAttribute('x2', x2);
line.setAttribute('y2', y);
line.setAttribute('style', 'stroke:#E6EBF5;stroke-width:1');
line.setAttribute('marker-end', 'url(#marker_end)');
line.setAttribute('marker-start', 'url(#marker_start)');

const text = document.createElementNS(this.svg.namespaceURI, 'text');
text.textContent = `${data.duration.toFixed(4)}ms`;
const textWidth = this.getTextWidth(text.textContent);
text.setAttribute('x', Math.max(0, (x2 - x1) / 2 + x1 - textWidth / 2));
text.setAttribute('y', y - 6);
text.setAttribute('font-size', 12);
text.setAttribute('fill', '#6c7280');

const startLine = document.createElementNS(this.svg.namespaceURI, 'line');
startLine.setAttribute('x1', x1 - this.svg.markerPadding);
startLine.setAttribute('y1', y - this.svg.rowHeight / 4);
startLine.setAttribute('x2', x1 - this.svg.markerPadding);
startLine.setAttribute('y2', y + this.svg.rowHeight / 4);
startLine.setAttribute('style', 'stroke:#E6EBF5;stroke-width:1');
g.appendChild(startLine);

const endLine = document.createElementNS(this.svg.namespaceURI, 'line');
endLine.setAttribute('x1', x1 + width - this.svg.markerPadding);
endLine.setAttribute('y1', y - this.svg.rowHeight / 4);
endLine.setAttribute('x2', x1 + width - this.svg.markerPadding);
endLine.setAttribute('y2', y + this.svg.rowHeight / 4);
endLine.setAttribute('style', 'stroke:#E6EBF5;stroke-width:1');
g.appendChild(endLine);
g.appendChild(line);
g.appendChild(text);
return g;
},
getTextWidth(text) {
const body = document.querySelector('body');
const temp = document.createElement('span');
temp.style['font-size'] = '12px';
temp.textContent = text;
body.appendChild(temp);
const textWidth = temp.offsetWidth;
body.removeChild(temp);
return textWidth;
},
removeTrace() {
const svgDom = document.querySelector('#trace svg');
if (svgDom) {
const gDoms = svgDom.children;
if (gDoms) {
for (let i = 0; i < gDoms.length; i++) {
if (gDoms[i].nodeName === 'g') {
svgDom.removeChild(gDoms[i--]);
}
}
}
}
},
resizeTrace() {
if (this.svg.resizeTimer) {
clearTimeout(this.svg.resizeTimer);
}
this.svg.resizeTimer = setTimeout(() => {
this.removeTrace();
this.dealTraceData();
this.svg.resizeTimer = null;
}, 500);
},
stringToUint8Array(str) {
const arr = [];
for (let i = 0, strLen = str.length; i < strLen; i++) {
arr.push(str.charCodeAt(i));
}
return new Uint8Array(arr);
},
},
destroyed() {
window.removeEventListener('resize', this.resizeTrace, false);
this.$bus.$off('resize');
},
};
</script>
<style lang="scss">
.pro-router-wrap {
height: 100%;
& > div {
float: left;
height: 100%;
& > div {
border: 1px solid #ddd;
border-radius: 4px;
}
.title-wrap {
padding: 15px;
.title {
float: left;
font-weight: bold;
font-size: 16px;
}
.tip-icon {
float: right;
margin-right: 18px;
font-size: 20px;
.el-icon-warning {
cursor: pointer;
&:hover::before {
color: #00a5a7;
}
}
}
.view-detail {
float: right;
cursor: pointer;
color: #00a5a7;
font-size: 12px;
height: 24px;
line-height: 24px;
a {
color: #00a5a7 !important;
padding-right: 6px;
}
button {
color: #00a5a7;
border: none;
background-color: #fff;
cursor: pointer;
}
button.disabled {
cursor: not-allowed;
color: #c0c4cc;
}
}
&::after {
content: '';
clear: both;
display: block;
}
}
.coming-soon-content {
height: calc(100% - 50px);
position: relative;
.coming-soon-container {
text-align: center;
position: absolute;
top: 50%;
left: 50%;
border-radius: 5px;
-webkit-transform: translate(-50%, -50%);
-moz-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.coming-soon-text {
font-size: 16px;
}
}
}
.pro-router-left {
width: calc(100% - 350px);
padding-right: 15px;
.step-trace {
height: 45%;
margin-bottom: 15px;
.trace-container {
width: 100%;
height: calc(100% - 50px);
overflow: auto;
.training-trace {
position: relative;
height: 0;
}
}
}
.minddata {
height: calc(55% - 15px);
}
}
.pro-router-right {
width: 350px;
.op-time-consume {
height: calc(60% - 15px);
margin-bottom: 15px;
.time-list {
height: calc(40% - 52px);
.item {
height: 25px;
line-height: 25px;
padding: 0 20px;
& > span {
display: inline-block;
height: 100%;
vertical-align: middle;
}
.index {
color: white;
background-color: rgb(108, 146, 250);
width: 20px;
height: 20px;
border-radius: 20px;
text-align: center;
vertical-align: middle;
line-height: 20px;
}
.name {
margin-left: 10px;
width: calc(50% - 30px);
text-overflow: ellipsis;
overflow: hidden;
}
.num {
width: 20%;
}
.time {
width: 30%;
position: relative;
span {
display: inline-block;
position: absolute;
left: 0;
height: 20px;
}
.bar {
background-color: #cceded;
top: 2px;
}
.value {
line-height: 25px;
height: 25px;
}
}
}
}
}
.time-line {
height: 40%;
overflow: hidden;
}
}
.op-time-content {
height: calc(100% - 54px);
overflow: auto;
}
.pie-chart {
width: 100%;
height: 260px;
}
.image-noData {
width: 100%;
height: calc(100% - 52px);
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
p {
font-size: 16px;
padding-top: 10px;
}
}
}
</style>

+ 208
- 0
mindinsight/ui/src/views/train-manage/profiling.vue View File

@@ -0,0 +1,208 @@
<!--
Copyright 2020 Huawei Technologies Co., Ltd.All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<template>
<div class="prof-wrap">
<div class="prof-content">
<div class="prof-content-left"
:class="{collapse:collapse}">
<div class="helper"
v-show="!collapse">
<div class="cur-card">
<label>{{$t('profiling.curCard')}}</label>
<el-select v-model="curDashboardInfo.curCardNum"
class="card-select"
:placeholder="$t('public.select')">
<el-option v-for="item in CardNumArr"
:key="item.value"
:label="item.value + $t('operator.card')"
:value="item.value">
</el-option>
</el-select>
</div>
<div class="helper-title">
{{$t("profiling.smartHelper")}}
</div>
</div>
<div class="collapse-btn" :class="{collapse:collapse}"
@click="collapseLeft()">
</div>
</div>
<div class="prof-content-right"
:class="{collapse:collapse}">
<router-view></router-view>
<div class="close"
@click="backToDdashboard"
v-if="$route.path !== '/profiling/profiling-dashboard'">
<img src="@/assets/images/close-page.png">
</div>
</div>
</div>
</div>
</template>
<script>
import RequestService from '../../services/request-service';
export default {
data() {
return {
CardNumArr: [],
collapse: false,
curDashboardInfo: {
curCardNum: '',
query: {},
},
};
},
watch: {},
mounted() {
this.$nextTick(() => {
this.init();
});
},
methods: {
init() {
if (this.$route.query && this.$route.query.id && this.$route.query.dir) {
this.curDashboardInfo.query.id = this.$route.query.id;
this.curDashboardInfo.query.dir = this.$route.query.dir;
this.curDashboardInfo.query.path = this.$route.query.path;
this.getDeviceList();
} else {
this.curDashboardInfo.query.trainingJobId = '';
this.curDashboardInfo.query.dir = '';
this.curDashboardInfo.query.path = '';
this.$message.error(this.$t('trainingDashboard.invalidId'));
}
},
getDeviceList() {
const params = {
profile: this.curDashboardInfo.query.dir,
train_id: this.curDashboardInfo.query.id,
};
RequestService.getProfilerDeviceData(params)
.then((res) => {
if (res && res.data) {
const deviceList = res.data;
if (deviceList.length) {
deviceList.forEach((item) => {
this.CardNumArr.push({
value: item,
});
});
this.curDashboardInfo.curCardNum = this.CardNumArr[0].value;
}
} else {
this.CardNumArr = [];
this.curDashboardInfo.curCardNum = '';
}
})
.catch(() => {});
},
backToDdashboard() {
this.$router.push({
path: '/profiling/profiling-dashboard',
query: {
dir: this.curDashboardInfo.query.dir,
id: this.curDashboardInfo.query.id,
path: this.curDashboardInfo.query.path,
},
});
},
collapseLeft() {
this.collapse = !this.collapse;
this.$bus.$emit('resize');
},
},
};
</script>
<style lang="scss">
.prof-wrap {
height: 100%;
background: #fff;
.prof-content {
height: 100%;
padding: 32px 32px 32px 0;
& > div {
float: left;
height: 100%;
}
.prof-content-left {
width: 25%;
transition: width 0.2s;
position: relative;
.el-input__inner {
padding: 0 10px;
}
.helper {
padding: 32px;
height: 100%;
margin-left: 32px;
background: #edf0f5;
.cur-card {
margin-bottom: 32px;
.card-select {
width: calc(100% - 70px);
}
& > label {
margin-right: 14px;
}
}
.helper-title {
font-size: 20px;
font-weight: bold;
margin-bottom: 32px;
.el-icon-rank {
float: right;
cursor: pointer;
}
}
}
.collapse-btn {
position: absolute;
right: -21px;
width: 31px;
height: 100px;
top: 50%;
margin-top: -50px;
cursor: pointer;
line-height: 86px;
z-index: 1;
text-align: center;
background-image: url('../../assets/images/collapse-left.svg');
}
.collapse-btn.collapse{
background-image: url('../../assets/images/collapse-right.svg');
}
}
.prof-content-left.collapse {
width: 0;
}
.prof-content-right {
width: 75%;
padding-left: 20px;
transition: width 0.2s;
position: relative;
.close {
position: absolute;
right: 0;
top: -10px;
cursor: pointer;
}
}
.prof-content-right.collapse {
width: 100%;
}
}
}
</style>

+ 660
- 0
mindinsight/ui/src/views/train-manage/step-trace.vue View File

@@ -0,0 +1,660 @@
<!--
Copyright 2020 Huawei Technologies Co., Ltd.All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<template>
<div class="step-trace">
<div class="step-trace-title">{{$t('profiling.stepTraceDetail')}}
<div class="pf-content-right">
<div class="input-wrap">
<label>{{$t('profiling.stepSelect')}}</label>
<el-select v-model="selectedStep"
filterable
:placeholder="$t('profiling.selectStep')"
@change="changeStep">
<el-option v-for="item in steps"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
</div>
</div>
<div class="pf-content-middle"
v-show="!tabsArr[0].noData && !tabsArr[1].noData && !tabsArr[2].noData && !svg.noData">
<div id="trace-container">
<div id="trace"
class="training-trace">
<div :title="$t('graph.downloadPic')"
class="download-button"
@click="downloadSVG">
</div>
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg"
height="100%"
width="100%">
<defs>
<marker id="marker_end"
refX="5"
refY="4"
markerWidth="10"
markerHeight="8"
orient="auto">
<path d="M1,1 L1,7 L9,4 z"
fill="#E6EBF5"
stroke="#E6EBF5"></path>
</marker>
<marker id="marker_start"
refX="5"
refY="4"
markerWidth="10"
markerHeight="8"
orient="auto">
<path d="M9,1 L9,7 L1,4 z"
fill="#E6EBF5"
stroke="#E6EBF5"></path>
</marker>
</defs>
</svg>
</div>
<div class="image-noData svg"
v-if="svg.data.length === 0">
<div>
<img :src="require('@/assets/images/nodata.png')"
alt="" />
</div>
<p>{{$t("public.noData")}}</p>
</div>
</div>

<div v-for="(item,key) in tabsArr"
:key="key"
class="chart-wrap">
<div class="title">{{ item.name }}</div>
<div class="rate-wrap">
<div v-if="item.timeSummary.total_time !== undefined">
<span>{{item.timeLabel}}:</span>{{item.timeSummary.total_time}}ms</div>
<div v-if="item.timeSummary[item.rate] !== undefined">
<span>{{item.rateLabel}}:</span>{{item.timeSummary[item.rate]}}</div>
<div v-if="item.timeSummary.total_steps !== undefined">
<span>{{$t('profiling.stepNum')}}:</span>{{item.timeSummary.total_steps}}</div>
</div>
<div class="chart"
:id="item.id"
v-show="!item.noData"></div>
<div class="image-noData"
v-if="item.noData">
<div>
<img :src="require('@/assets/images/nodata.png')"
alt="" />
</div>
<p>{{$t("public.noData")}}</p>
</div>
</div>
</div>
<div class="image-noData"
v-if="!(!tabsArr[0].noData && !tabsArr[1].noData && !tabsArr[2].noData && !svg.noData)">
<div>
<img :src="require('@/assets/images/nodata.png')"
alt="" />
</div>
<p>{{$t("public.noData")}}</p>
</div>
</div>
</template>

<script>
import echarts from 'echarts';
import RequestService from '../../services/request-service';
export default {
data() {
return {
dir: this.$route.query.dir,
train_id: this.$route.query.id,
relativePath: this.$route.query.path,
steps: [],
selectedStep: '',
charts: [],
svg: {
data: [],
svgPadding: 20,
totalWidth: 0,
totalTime: 0,
rowHeight: 60,
markerPadding: 4,
namespaceURI: 'http://www.w3.org/2000/svg',
resizeTimer: null,
colorList: [
['#A6DD82', '#edf8e6'],
['#6CBFFF', '#e2f2ff'],
['#fa8e5b', '#fff4de'],
['#01a5a7', '#cceded'],
],
colorIndex: 0,
noData: false,
},
deviceId: 0,
radio: this.$t('profiling.lterationGap'),
tabsArr: [
{
name: this.$t('profiling.lterationGap'),
id: 'iter-gap',
timeSummary: {},
rate: 'iteration_interval',
timeLabel: this.$t('profiling.iterGapTimeLabel'),
rateLabel: this.$t('profiling.iterGapRateLabel'),
noData: false,
},
{
name: 'Fp+bp',
id: 'fp-bp',
timeSummary: {},
rate: 'fp_and_bp',
timeLabel: this.$t('profiling.fpBpTimeLabel'),
rateLabel: this.$t('profiling.fpBpRateLabel'),
noData: false,
},
{
name: this.$t('profiling.lterationTail'),
id: 'tailing',
timeSummary: {},
rate: 'tail',
timeLabel: this.$t('profiling.tailTimeLabel'),
rateLabel: this.$t('profiling.tailRateLabel'),
noData: false,
},
],
};
},
watch: {
'$parent.curDashboardInfo': {
handler(newValue, oldValue) {
if (newValue.curCardNum || newValue.curCardNum === 0) {
this.dir = newValue.query.dir;
this.train_id = newValue.query.id;
this.deviceId = newValue.curCardNum;
this.relativePath = newValue.query.path;
if (this.train_id) {
document.title = `${decodeURIComponent(this.train_id)}-${this.$t(
'profiling.stepTrace',
)}-MindInsight`;
} else {
document.title = `${this.$t('profiling.stepTrace')}-MindInsight`;
}
this.svg.noData = false;
this.tabsArr.forEach((val) => {
val.noData = false;
});
this.init();
}
},
deep: true,
immediate: true,
},
},
computed: {},
mounted() {},
methods: {
init() {
window.addEventListener('resize', this.resizeTrace, false);
this.$bus.$on('resize', this.resizeTrace);
window.addEventListener('resize', this.resizeEchart, false);
this.$bus.$on('resize', this.resizeEchart);
if (this.charts.length) {
this.charts.forEach((val) => {
val.clear();
});
}
this.setStep();
this.getTimeInfo('fp-bp', 'fp_and_bp');
this.getTimeInfo('iter-gap', 'iteration_interval');
this.getTimeInfo('tailing', 'tail');
this.queryTrainingTrace(0);
},
changeStep(value) {
if (value === this.$t('profiling.showAverage')) {
value = 0;
}
this.queryTrainingTrace(value);
},
getTimeInfo(id, type) {
const params = {
dir: this.relativePath,
type,
device_id: this.deviceId,
};
RequestService.targetTimeInfo(params).then(
(res) => {
if (res.data && res.data.summary) {
const summary = res.data.summary;
Object.keys(summary).forEach((val) => {
summary[val] = summary[val];
});
this.tabsArr.forEach((val) => {
if (id === val.id) {
val.timeSummary = summary;
}
});
}
if (res.data && res.data.info) {
if (this.steps.length <= 1) {
this.setStep(res.data.size);
}
const timeInfo = [];
Object.keys(res.data.info).forEach((val) => {
timeInfo.push({
data: res.data.info[val],
name: val,
type: 'line',
});
});
if (timeInfo.length) {
const option = {
xAxis: {
type: 'category',
data: this.steps.map((val, index) => index + 1),
name: 'step',
},
yAxis: {
type: 'value',
name: '',
nameTextStyle: {
padding: [0, 0, 0, 30],
},
},
grid: {
left: 50,
top: 50,
right: 50,
bottom: 20,
},
series: timeInfo,
tooltip: {
trigger: 'axis',
},
};
if (type === 'iteration_interval') {
option.yAxis.name = `${this.$t(
'profiling.iterationGapTime',
)}(ms)`;
this.tabsArr[0].noData = this.steps.length ? false : true;
} else if (type === 'fp_and_bp') {
option.yAxis.name = `fp+bp${this.$t('profiling.time')}(ms)`;
this.tabsArr[1].noData = this.steps.length ? false : true;
} else if (type === 'tail') {
option.yAxis.name = `tail${this.$t('profiling.time')}(ms)`;
this.tabsArr[2].noData = this.steps.length ? false : true;
}
this.initChart(option, id);
} else {
this.steps = [];
this.selectedStep = '';
}
}
},
(error) => {
this.steps = [];
this.selectedStep = '';
if (type === 'iteration_interval') {
this.tabsArr[0].noData = true;
} else if (type === 'fp_and_bp') {
this.tabsArr[1].noData = true;
} else if (type === 'tail') {
this.tabsArr[2].noData = true;
}
},
);
},
setStep(step = 0) {
this.steps = [];
this.steps.push({
label: this.$t('profiling.showAverage'),
value: this.$t('profiling.showAverage'),
});
for (let i = 1; i <= step; i++) {
this.steps.push({
label: i,
value: i,
});
}
this.selectedStep = this.$t('profiling.showAverage');
},
initChart(option, id) {
this.$nextTick(() => {
const chart = echarts.init(document.getElementById(id));
chart.setOption(option, true);
this.charts.push(chart);
});
},
resizeEchart() {
setTimeout(() => {
this.charts.forEach((val)=>{
val.resize();
});
}, 300);
},
queryTrainingTrace(step) {
const params = {
dir: this.relativePath,
type: step,
device_id: this.deviceId,
};
RequestService.queryTrainingTrace(params).then(
(res) => {
if (
res.data &&
res.data.training_trace_graph &&
res.data.training_trace_graph.length
) {
this.svg.noData = false;
document.querySelector('#trace').style.height = `${res.data
.training_trace_graph.length * this.svg.rowHeight}px`;
this.svg.data = JSON.parse(
JSON.stringify(res.data.training_trace_graph),
);
this.removeTrace();
setTimeout(() => {
this.dealTraceData();
}, 100);
} else {
this.svg.data = [];
this.svg.noData = true;
this.removeTrace();
}
},
(error) => {
this.svg.data = [];
this.svg.noData = true;
this.removeTrace();
},
);
},

dealTraceData() {
this.svg.totalWidth =
document.querySelector('#trace').offsetWidth - this.svg.svgPadding * 2;
if (this.svg.data[0] && this.svg.data[0].length) {
const svg = document.querySelector('#trace svg');
this.svg.totalTime = this.svg.data[0][0].duration;
if (this.svg.totalTime) {
this.svg.data.forEach((row, index) => {
if (row && row.length) {
const dashedLine = this.addDashedLine(index);
svg.insertBefore(dashedLine, svg.querySelector('g'));
row.forEach((i) => {
if (i.duration) {
const tempDom = i.name
? this.createRect(i, index)
: this.createArrow(i, index);
svg.insertBefore(tempDom, svg.querySelector('g'));
}
});
}
});
}
} else {
this.removeTrace();
}
},
addDashedLine(index) {
const x1 = this.svg.svgPadding;
const x2 = this.svg.svgPadding + this.svg.totalWidth;
const y = index * this.svg.rowHeight;
const line = document.createElementNS(this.svg.namespaceURI, 'line');
line.setAttribute('x1', x1);
line.setAttribute('y1', y);
line.setAttribute('x2', x2);
line.setAttribute('y2', y);
line.setAttribute('style', 'stroke:#E2E2E2;stroke-width:1');
line.setAttribute('stroke-dasharray', '5 5');
const g = document.createElementNS(this.svg.namespaceURI, 'g');
g.appendChild(line);
return g;
},
createRect(data, rowIndex) {
const color = this.svg.colorList[this.svg.colorIndex++ % 4];
const height = 40;
const width = (data.duration / this.svg.totalTime) * this.svg.totalWidth;
const x1 =
(data.start / this.svg.totalTime) * this.svg.totalWidth +
this.svg.svgPadding;
const y1 =
rowIndex * this.svg.rowHeight + (this.svg.rowHeight - height) / 2;
const g = document.createElementNS(this.svg.namespaceURI, 'g');
const gChild = document.createElementNS(this.svg.namespaceURI, 'g');

const rect = document.createElementNS(this.svg.namespaceURI, 'rect');
rect.setAttribute('x', x1);
rect.setAttribute('y', y1);
rect.setAttribute('height', height);
rect.setAttribute('width', width);
rect.setAttribute('style', `fill:${color[1]};stroke:${color[1]};`);

const foreignObject = document.createElementNS(
this.svg.namespaceURI,
'foreignObject',
);
foreignObject.setAttribute('x', x1);
foreignObject.setAttribute('y', y1);
foreignObject.setAttribute('height', height);
foreignObject.setAttribute('width', width);
foreignObject.setAttribute(
'style',
`overflow:hidden;text-align:center;text-overflow:ellipsis;` +
`white-space:nowrap;font-size:12px;line-height:${height}px;color:${color[0]}`,
);
foreignObject.textContent = `${data.name}: ${data.duration.toFixed(4)}ms`;

const title = document.createElementNS(this.svg.namespaceURI, 'title');
title.textContent = `${data.name}: ${data.duration.toFixed(4)}ms`;

gChild.appendChild(rect);
gChild.appendChild(foreignObject);
gChild.appendChild(title);
g.appendChild(gChild);
return g;
},

createArrow(data, rowIndex) {
const width = (data.duration / this.svg.totalTime) * this.svg.totalWidth;
const x1 =
(data.start / this.svg.totalTime) * this.svg.totalWidth +
this.svg.markerPadding +
this.svg.svgPadding;
const x2 = x1 + width - this.svg.markerPadding * 2;
const y = rowIndex * this.svg.rowHeight + this.svg.rowHeight / 2;
const g = document.createElementNS(this.svg.namespaceURI, 'g');

const line = document.createElementNS(this.svg.namespaceURI, 'line');
line.setAttribute('x1', x1);
line.setAttribute('y1', y);
line.setAttribute('x2', x2);
line.setAttribute('y2', y);
line.setAttribute('style', 'stroke:#E6EBF5;stroke-width:1');
line.setAttribute('marker-end', 'url(#marker_end)');
line.setAttribute('marker-start', 'url(#marker_start)');

const text = document.createElementNS(this.svg.namespaceURI, 'text');
text.textContent = `${data.duration.toFixed(4)}ms`;
const textWidth = this.getTextWidth(text.textContent);
text.setAttribute('x', Math.max(0, (x2 - x1) / 2 + x1 - textWidth / 2));
text.setAttribute('y', y - 6);
text.setAttribute('font-size', 12);
text.setAttribute('fill', '#6c7280');

const startLine = document.createElementNS(this.svg.namespaceURI, 'line');
startLine.setAttribute('x1', x1 - this.svg.markerPadding);
startLine.setAttribute('y1', y - this.svg.rowHeight / 4);
startLine.setAttribute('x2', x1 - this.svg.markerPadding);
startLine.setAttribute('y2', y + this.svg.rowHeight / 4);
startLine.setAttribute('style', 'stroke:#E6EBF5;stroke-width:1');
g.appendChild(startLine);

const endLine = document.createElementNS(this.svg.namespaceURI, 'line');
endLine.setAttribute('x1', x1 + width - this.svg.markerPadding);
endLine.setAttribute('y1', y - this.svg.rowHeight / 4);
endLine.setAttribute('x2', x1 + width - this.svg.markerPadding);
endLine.setAttribute('y2', y + this.svg.rowHeight / 4);
endLine.setAttribute('style', 'stroke:#E6EBF5;stroke-width:1');
g.appendChild(endLine);
g.appendChild(line);
g.appendChild(text);
return g;
},
getTextWidth(text) {
const body = document.querySelector('body');
const temp = document.createElement('span');
temp.style['font-size'] = '12px';
temp.textContent = text;
body.appendChild(temp);
const textWidth = temp.offsetWidth;
body.removeChild(temp);
return textWidth;
},
removeTrace() {
const svgDom = document.querySelector('#trace svg');
if (svgDom) {
const gDoms = svgDom.children;
if (gDoms) {
for (let i = 0; i < gDoms.length; i++) {
if (gDoms[i].nodeName === 'g') {
svgDom.removeChild(gDoms[i--]);
}
}
}
}
},
resizeTrace() {
if (this.svg.resizeTimer) {
clearTimeout(this.svg.resizeTimer);
}
this.svg.resizeTimer = setTimeout(() => {
this.removeTrace();
this.dealTraceData();
this.svg.resizeTimer = null;
}, 500);
},
downloadSVG() {
const svgDom = document.querySelector('svg').outerHTML;
const src = `data:image/svg+xml;base64,
${window.btoa(unescape(encodeURIComponent(svgDom)))}`;
const a = document.createElement('a');
a.href = src;
a.download = new Date().valueOf();
a.click();
},
},
destroyed() {
window.removeEventListener('resize', this.resizeTrace, false);
window.removeEventListener('resize', this.resizeEchart, false);
this.$bus.$off('resize');
},
};
</script>
<style lang="scss">
.step-trace {
width: 100%;
height: 100%;
.step-trace-title {
padding: 0 15px;
font-size: 16px;
font-weight: bold;
.pf-content-right {
display: inline-block;
margin-left: 35px;
label {
margin-right: 10px;
}
}
.input-wrap {
font-weight: normal;
}
}
.pf-content-middle {
padding: 15px 15px 0;
height: calc(100% - 32px);
#trace-container {
width: 100%;
height: 50%;
border: 1px solid #ccc;
overflow: auto;
.training-trace {
position: relative;
height: 0;
.download-button {
display: none;
position: absolute;
width: 12px;
height: 12px;
right: 10px;
top: 10px;
cursor: pointer;
background-image: url('../../assets/images/download.png');
}
}
}
.chart-wrap {
float: left;
height: calc(50% - 20px);
margin-top: 20px;
margin-right: 15px;
width: calc(33.3% - 10px);
border: 1px solid #ccc;
padding: 30px;
border-radius: 4px;
overflow: auto;
&:last-child {
margin-right: 0;
}
.chart {
height: calc(100% - 85px);
min-height: 180px;
}
.title {
margin: 0 0 15px 20px;
font-weight: bold;
}
.rate-wrap {
font-size: 12px;
padding-left: 20px;
& > div {
display: inline-block;
margin: 0 15px 5px 0;
color: #464950;
span {
margin-right: 10px;
color: #6c7280;
}
}
}
}
}
.image-noData {
width: 100%;
height: calc(100% - 52px);
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
p {
font-size: 16px;
padding-top: 10px;
}
}
.image-noData.svg {
height: 100%;
}
}
</style>

Loading…
Cancel
Save