| @@ -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> | |||
| @@ -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> | |||
| @@ -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> | |||
| @@ -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> | |||
| @@ -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> | |||