• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15import { BaseElement, element } from '../../../../../base-ui/BaseElement';
16import { type LitTable, RedrawTreeForm } from '../../../../../base-ui/table/lit-table';
17import { SelectionParam } from '../../../../bean/BoxSelection';
18import { getGpufreqData, getGpufreqDataCut } from '../../../../database/sql/Perf.sql';
19import { resizeObserver } from '../SheetUtils';
20import { SpSegmentationChart } from '../../../chart/SpSegmentationChart';
21import { GpuCountBean, TreeDataBean, type SearchGpuFuncBean, CycleDataBean, TreeDataStringBean } from '../../../../bean/GpufreqBean';
22
23@element('tabpane-gpufreqdatacut')
24export class TabPaneGpufreqDataCut extends BaseElement {
25  private threadStatesTbl: LitTable | null | undefined;
26  private currentSelectionParam: SelectionParam | undefined;
27  private _single: Element | null | undefined;
28  private _loop: Element | null | undefined;
29  private _threadId: HTMLInputElement | null | undefined;
30  private _threadFunc: HTMLInputElement | null | undefined;
31  private threadIdValue: string = '';
32  private threadFuncName: string = '';
33  private initData: Array<GpuCountBean> = [];
34  private SUB_LENGTH: number = 3;
35  private PERCENT_SUB_LENGTH: number = 2;
36  private UNIT: number = 1000000;
37  private KUNIT: number = 1000000000000;
38
39  set data(threadStatesParam: SelectionParam) {
40    if (this.currentSelectionParam === threadStatesParam) {
41      return;
42    } else {
43      SpSegmentationChart.setChartData('GPU-FREQ', []);
44    };
45    this.currentSelectionParam = threadStatesParam;
46    this.threadStatesTbl!.recycleDataSource = [];
47    this.threadStatesTbl!.loading = true;
48    this.getGpufreqData(threadStatesParam.leftNs, threadStatesParam.rightNs, false).then((result) => {
49      if (result !== null && result.length > 0) {
50        let resultList: Array<GpuCountBean> = JSON.parse(JSON.stringify(result));
51        resultList[0].dur = resultList[1] ? resultList[1].startNS - threadStatesParam.leftNs : threadStatesParam.rightNs - threadStatesParam.leftNs;
52        resultList[0].value = resultList[0].dur * resultList[0].val;
53        resultList[resultList.length - 1].dur = resultList.length - 1 !== 0 ? threadStatesParam.rightNs - resultList[resultList.length - 1].startNS : resultList[0].dur;
54        resultList[resultList.length - 1].value = resultList.length - 1 !== 0 ? resultList[resultList.length - 1].dur * resultList[resultList.length - 1].val : resultList[0].value;
55        this.initData = resultList;
56        this.threadStatesTbl!.loading = false;
57      } else {
58        this.threadStatesTbl!.recycleDataSource = [];
59        this.threadStatesTbl!.loading = false;
60      };
61    });
62    this._threadId!.style.border = '1px solid rgb(151, 151, 151)';
63    this._threadFunc!.style.border = '1px solid rgb(151, 151, 151)';
64    this.isChangeSingleBtn(false);
65    this.isChangeLoopBtn(false);
66  };
67
68  initElements(): void {
69    this.threadStatesTbl = this.shadowRoot?.querySelector<LitTable>('#tb-gpufreq-percent');
70    this._single = this.shadowRoot?.querySelector('#single');
71    this._loop = this.shadowRoot?.querySelector('#loop');
72    this._threadId = this.shadowRoot?.querySelector('#dataCutThreadId');
73    this._threadFunc = this.shadowRoot?.querySelector('#dataCutThreadFunc');
74    this.threadIdValue = this._threadId!.value.trim();
75    this.threadFuncName = this._threadFunc!.value.trim();
76    //点击single
77    this._single?.addEventListener('click', (e) => {
78      this.isChangeSingleBtn(true);
79      this.isChangeLoopBtn(false);
80      this.clickFun(this._single!.innerHTML);
81    });
82    //点击loop
83    this._loop?.addEventListener('click', (e) => {
84      this.isChangeSingleBtn(false);
85      this.isChangeLoopBtn(true);
86      this.clickFun(this._loop!.innerHTML);
87    });
88    //点击周期,算力泳道对应周期实现高亮效果
89    this.threadStatesTbl?.addEventListener('row-click', (event: Event) => {
90      const EVENT_LEVEL: string = '2';
91      // @ts-ignore
92      if (event.detail.level === EVENT_LEVEL && event.detail.thread.includes('cycle')) {
93        // @ts-ignore
94        SpSegmentationChart.tabHover('GPU-FREQ', true, event.detail.data.cycle);
95      };
96    });
97    this.addInputBorderEvent(this._threadId!);
98    this.addInputBorderEvent(this._threadFunc!);
99  };
100  async getGpufreqData(leftNs: number, rightNs: number, isTrue: boolean): Promise<Array<GpuCountBean>> {
101    let result: Array<GpuCountBean> = await getGpufreqData(leftNs, rightNs, isTrue);
102    return result;
103  };
104  async getGpufreqDataCut(tIds: string, funcName: string, leftNS: number, rightNS: number, single: boolean, loop: boolean): Promise<Array<SearchGpuFuncBean>> {
105    let result: Array<SearchGpuFuncBean> = await getGpufreqDataCut(tIds, funcName, leftNS, rightNS, single, loop);
106    return result;
107  };
108  //是否改变single按钮颜色
109  private isChangeSingleBtn(flag: boolean): void {
110    if (flag) {
111      this.setAttribute('single', '');
112    } else {
113      this.removeAttribute('single');
114    };
115  }
116  //是否改变loop按钮颜色
117  private isChangeLoopBtn(flag: boolean): void {
118    if (flag) {
119      this.setAttribute('loop', '');
120    } else {
121      this.removeAttribute('loop');
122    };
123  }
124  private clickFun(fun: string): void {
125    this.threadIdValue = this._threadId!.value.trim();
126    this.threadFuncName = this._threadFunc!.value.trim();
127    this.threadStatesTbl!.loading = true;
128    SpSegmentationChart.tabHover('GPU-FREQ', false, -1);
129    this.validationFun(this.threadIdValue, this.threadFuncName, fun);
130  };
131  private addInputBorderEvent(inputElement: HTMLInputElement): void {
132    if (inputElement) {
133      inputElement.addEventListener('change', function () {
134        if (this.value.trim() !== '') {
135          this.style.border = '1px solid rgb(151, 151, 151)';
136        }
137      });
138    }
139  };
140  private validationFun(threadIdValue: string, threadFuncName: string, fun: string): void {
141    if (threadIdValue === '') {
142      this.handleEmptyInput(this._threadId!);
143    } else if (threadFuncName === '') {
144      this.handleEmptyInput(this._threadFunc!);
145    } else {
146      this._threadId!.style.border = '1px solid rgb(151, 151, 151)';
147      this._threadFunc!.style.border = '1px solid rgb(151, 151, 151)';
148      if (fun === 'Single') {
149        this.isTrue(threadIdValue, threadFuncName, true, false);
150      };
151      if (fun === 'Loop') {
152        this.isTrue(threadIdValue, threadFuncName, false, true);
153      };
154    };
155  };
156  private handleEmptyInput(input: HTMLInputElement): void {
157    this.threadStatesTbl!.loading = false;
158    input!.style.border = '1px solid rgb(255,0,0)';
159    this.threadStatesTbl!.recycleDataSource = [];
160  };
161  private isTrue(threadIdValue: string, threadFuncName: string, single: boolean, loop: boolean): void {
162    this.getGpufreqDataCut(threadIdValue, threadFuncName,
163      this.currentSelectionParam!.leftNs,
164      this.currentSelectionParam!.rightNs,
165      single, loop
166    ).then((result: Array<SearchGpuFuncBean>) => {
167      let _initData = JSON.parse(JSON.stringify(this.initData));
168      this.handleDataCut(_initData, result);
169    });
170  };
171  private handleDataCut(initData: Array<GpuCountBean>, dataCut: Array<SearchGpuFuncBean>): void {
172    if (initData.length > 0 && dataCut.length > 0) {
173      let finalGpufreqData: Array<TreeDataStringBean> = new Array();
174      let startPoint: number = initData[0].startNS;
175      let _dataCut: Array<SearchGpuFuncBean> = dataCut.filter((i) => i.startTime >= startPoint);
176      let _lastList: Array<GpuCountBean> = [];
177      let i: number = 0;
178      let j: number = 0;
179      let currentIndex: number = 0;
180      while (i < _dataCut.length) {
181        let dataItem: SearchGpuFuncBean = _dataCut[i];
182        let initItem: GpuCountBean = initData[j];
183        _lastList.push(...this.segmentationData(initItem, dataItem, i));
184        j++;
185        currentIndex++;
186        if (currentIndex === initData.length) {
187          i++;
188          j = 0;
189          currentIndex = 0;
190        };
191      };
192      let tree: TreeDataStringBean = this.createTree(_lastList);
193      finalGpufreqData.push(tree);
194      this.threadStatesTbl!.recycleDataSource = finalGpufreqData;
195      this.threadStatesTbl!.loading = false;
196      this.clickTableHeader(finalGpufreqData);
197
198    } else {
199      this.threadStatesTbl!.recycleDataSource = [];
200      this.threadStatesTbl!.loading = false;
201      SpSegmentationChart.setChartData('GPU-FREQ', []);
202    };
203  };
204  private segmentationData(j: GpuCountBean, e: SearchGpuFuncBean, i: number): Array<GpuCountBean> {
205    let lastList: Array<GpuCountBean> = [];
206    if (j.startNS <= e.startTime && j.endTime >= e.startTime) {
207      if (j.endTime >= e.endTime) {
208        lastList.push(
209          new GpuCountBean(
210            j.freq,
211            (e.endTime - e.startTime) * j.val,
212            j.val,
213            e.endTime - e.startTime,
214            e.startTime,
215            e.endTime,
216            j.thread,
217            i
218          )
219        );
220      } else {
221        lastList.push(
222          new GpuCountBean(
223            j.freq,
224            (j.endTime - e.startTime) * j.val,
225            j.val,
226            j.endTime - e.startTime,
227            e.startTime,
228            j.endTime,
229            j.thread,
230            i
231          )
232        );
233      };
234    } else if (j.startNS >= e.startTime && j.endTime <= e.endTime) {
235      lastList.push(
236        new GpuCountBean(
237          j.freq,
238          (j.endTime - j.startNS) * j.val,
239          j.val,
240          j.endTime - j.startNS,
241          j.startNS,
242          j.endTime,
243          j.thread,
244          i
245        )
246      );
247    } else if (j.startNS <= e.endTime && j.endTime >= e.endTime) {
248      lastList.push(
249        new GpuCountBean(
250          j.freq,
251          (e.endTime - j.startNS) * j.val,
252          j.val,
253          e.endTime - j.startNS,
254          j.startNS,
255          e.endTime,
256          j.thread,
257          i
258        )
259      );
260    };
261    return lastList;
262  };
263  // 创建树形结构
264  private createTree(data: Array<GpuCountBean>): TreeDataStringBean {
265    if (data.length > 0) {
266      const root: {
267        thread: string;
268        value: number;
269        dur: number;
270        percent: number;
271        level: number;
272        children: TreeDataBean[];
273      } = {
274        thread: 'gpufreq Frequency',
275        value: 0,
276        dur: 0,
277        percent: 100,
278        level: 1,
279        children: [],
280      };
281      const valueMap: { [parentIndex: number]: TreeDataBean } = {};
282      data.forEach((item: GpuCountBean) => {
283        let parentIndex: number = item.parentIndex !== undefined ? item.parentIndex : 0;
284        let freq: number = item.freq;
285        item.thread = `${item.thread} Frequency`;
286        item.level = 4;
287        this.updateValueMap(item, parentIndex, freq, valueMap);
288      });
289      Object.values(valueMap).forEach((node: TreeDataBean) => {
290        const parentNode: TreeDataBean = valueMap[node.freq! - 1];
291        if (parentNode) {
292          parentNode.children.push(node);
293          parentNode.dur += node.dur;
294          parentNode.value += node.value;
295        } else {
296          root.children.push(node);
297          root.dur += node.dur;
298          root.value += node.value;
299        }
300      });
301      this.flattenAndCalculate(root, root);
302      const firstLevelChildren = this.getFirstLevelChildren(root);
303      SpSegmentationChart.setChartData('GPU-FREQ', firstLevelChildren);
304      let _root = this.RetainDecimals(root)
305      return _root;
306    } else {
307      return new TreeDataStringBean('', '', '', '', '', '');
308    };
309  };
310  private updateValueMap(item: GpuCountBean, parentIndex: number, freq: number, valueMap: { [parentIndex: string]: TreeDataBean }): void {
311    if (!valueMap[parentIndex]) {
312      valueMap[parentIndex] = {
313        thread: `cycle ${parentIndex + 1} ${item.thread}`,
314        value: item.value,
315        dur: item.dur,
316        startNS: item.startNS,
317        percent: 100,
318        level: 2,
319        cycle: parentIndex + 1,
320        children: [],
321      };
322    } else {
323      valueMap[parentIndex].dur += item.dur;
324      valueMap[parentIndex].value += item.value;
325    };
326    if (!valueMap[parentIndex].children[freq]) {
327      valueMap[parentIndex].children[freq] = {
328        thread: item.thread,
329        value: item.value,
330        dur: item.dur,
331        percent: 100,
332        level: 3,
333        children: [],
334      };
335    } else {
336      valueMap[parentIndex].children[freq].dur += item.dur;
337      valueMap[parentIndex].children[freq].value += item.value;
338    };
339    valueMap[parentIndex].children[freq].children.push(item as unknown as TreeDataBean);
340  };
341  private getFirstLevelChildren(obj: TreeDataBean): Array<CycleDataBean> {
342    const result: Array<CycleDataBean> = [];
343    if (Array.isArray(obj.children)) {
344      obj.children.forEach((child) => {
345        if (child.cycle !== undefined && child.dur !== undefined && child.value !== undefined && child.startNS !== undefined) {
346          result.push(new CycleDataBean(7, child.dur, Number((child.value / this.KUNIT).toFixed(3)), child.startNS, child.cycle, '', 1));
347        };
348      });
349    };
350    return result;
351  };
352  private flattenAndCalculate(node: TreeDataBean, root: TreeDataBean): void {
353    node.percent = node.value / root.value * 100;
354    if (node.children) {
355      node.children = node.children.flat();
356      node.children.forEach((childNode) => this.flattenAndCalculate(childNode, root));
357    };
358  };
359  private RetainDecimals(root: TreeDataBean): TreeDataStringBean {
360    const treeDataString: TreeDataStringBean = new TreeDataStringBean(root.thread!, (root.value / this.KUNIT).toFixed(this.SUB_LENGTH), (root.dur / this.UNIT).toFixed(this.SUB_LENGTH), root.percent!.toFixed(this.PERCENT_SUB_LENGTH), String(root.level), '', 0, [], '', false);
361    if (root.children) {
362      for (const child of root.children) {
363        treeDataString.children!.push(this.convertChildToString(child) as TreeDataStringBean);
364      };
365    };
366    return treeDataString;
367  };
368  private convertChildToString(child: TreeDataBean | TreeDataBean[]): TreeDataStringBean | TreeDataStringBean[] {
369    if (Array.isArray(child)) {
370      if (child.length > 0) {
371        return child.map(c => this.convertChildToString(c) as TreeDataStringBean);
372      } else {
373        return [];
374      }
375    } else if (child && child.children) {
376      return {
377        thread: child.thread as string,
378        value: (child.value / this.KUNIT).toFixed(this.SUB_LENGTH),
379        freq: '',
380        cycle: child.cycle ? child.cycle : 0,
381        dur: (child.dur / this.UNIT).toFixed(this.SUB_LENGTH),
382        percent: child.percent ? child.percent.toFixed(this.PERCENT_SUB_LENGTH) : '',
383        level: String(child.level),
384        startNS: child.startNS ? (child.startNS / this.UNIT).toFixed(this.SUB_LENGTH) : '',
385        children: this.convertChildToString(child.children) as unknown as TreeDataStringBean[],
386      };
387    } else {
388      return {
389        thread: child.thread as string,
390        value: (child.value / this.KUNIT).toFixed(this.SUB_LENGTH),
391        freq: child.freq ? child.freq!.toFixed(this.SUB_LENGTH) : '',
392        dur: (child.dur / this.UNIT).toFixed(this.SUB_LENGTH),
393        percent: child.percent ? child.percent.toFixed(this.PERCENT_SUB_LENGTH) : '',
394        level: String(child.level)
395      };
396    }
397
398  };
399  // 表头点击事件
400  private clickTableHeader(data: Array<TreeDataStringBean>): void {
401    let labels = this.threadStatesTbl?.shadowRoot?.querySelector('.th > .td')!.querySelectorAll('label');
402    const THREAD_INDEX: number = 0;
403    const CYCLE_INDEX: number = 1;
404    const FREQ_INDEX: number = 2;
405    if (labels) {
406      for (let i = 0; i < labels.length; i++) {
407        let label = labels[i].innerHTML;
408        labels[i].addEventListener('click', (e) => {
409          if (label.includes('Thread') && i === THREAD_INDEX) {
410            this.threadStatesTbl!.setStatus(data, false);
411            this.threadStatesTbl!.recycleDs = this.threadStatesTbl!.meauseTreeRowElement(data, RedrawTreeForm.Retract);
412          } else if (label.includes('Cycle') && i === CYCLE_INDEX) {
413            for (let item of data) {
414              item.status = true;
415              if (item.children !== undefined && item.children.length > 0) {
416                this.threadStatesTbl!.setStatus(item.children, false);
417              };
418            };
419            this.threadStatesTbl!.recycleDs = this.threadStatesTbl!.meauseTreeRowElement(data, RedrawTreeForm.Retract);
420          } else if (label.includes('Freq') && i === FREQ_INDEX) {
421            for (let item of data) {
422              item.status = true;
423              for (let e of item.children ? item.children : []) {
424                e.status = true;
425                if (e.children !== undefined && e.children.length > 0) {
426                  this.threadStatesTbl!.setStatus(e.children, true);
427                };
428              };
429            };
430
431            this.threadStatesTbl!.recycleDs = this.threadStatesTbl!.meauseTreeRowElement(data, RedrawTreeForm.Expand);
432          };
433        });
434      };
435    };
436  };
437  connectedCallback(): void {
438    super.connectedCallback();
439    resizeObserver(this.parentElement!, this.threadStatesTbl!);
440  };
441
442  initHtml(): string {
443    return `<style>
444        :host{
445            padding: 10px 10px;
446            display: flex;
447            flex-direction: column;
448        }
449        #dataCut{
450            display: flex;
451            justify-content: space-between;
452            width:100%;
453            height:20px;
454            margin-bottom:2px;
455            align-items:center;
456        }
457        button{
458            width:40%;
459            height:100%;
460            border: solid 1px #666666;
461            background-color: rgba(0,0,0,0);
462            border-radius:10px;
463        }
464        button:hover{
465            background-color:#666666;
466            color:white;
467        }
468        :host([single]) #single {
469          background-color: #666666;
470          color: white
471        }
472        :host([loop]) #loop {
473          background-color: #666666;
474          color: white
475        }
476        </style>
477        <div id='dataCut'>
478            <input id="dataCutThreadId" type="text" style="width: 15%;height:90%;border-radius:10px;border:solid 1px #979797;font-size:15px;text-indent:3%" placeholder="Please input thread id" onkeyup="this.value=this.value.replace(/\\D/g,'')"/>
479            <input id="dataCutThreadFunc" type="text" style="width: 20%;height:90%;border-radius:10px;border:solid 1px #979797;font-size:15px;text-indent:3%" placeholder="Please input function name"/>
480            <div style="width:20%;height: 100%;display:flex;justify-content: space-around;">
481                <button id="single">Single</button>
482                <button id="loop">Loop</button>
483            </div>
484        </div>
485        <lit-table id="tb-gpufreq-percent" style="height: auto; overflow-x:auto;width:calc(100vw - 270px)" tree>
486            <lit-table-column class="running-percent-column" width="25%" title="Thread/Cycle/Freq" data-index="thread" key="thread" align="flex-start" retract>
487            </lit-table-column>
488            <lit-table-column class="running-percent-column" width="1fr" title="Cycle_st(ms)" data-index="startNS" key="startNS" align="flex-start">
489            </lit-table-column>
490            <lit-table-column class="running-percent-column" width="1fr" title="consumption(MHz·ms)" data-index="value" key="value" align="flex-start">
491            </lit-table-column>
492            <lit-table-column class="running-percent-column" width="1fr" title="Freq(MHz)" data-index="freq" key="freq" align="flex-start">
493            </lit-table-column>
494            <lit-table-column class="running-percent-column" width="1fr" title="dur(ms)" data-index="dur" key="dur" align="flex-start">
495            </lit-table-column>
496            <lit-table-column class="running-percent-column" width="1fr" title="Percent(%)" data-index="percent" key="percent" align="flex-start">
497            </lit-table-column>
498        </lit-table>`;
499  };
500}
501