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