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 */ 15 16import { BaseElement, element } from '../../../../../base-ui/BaseElement'; 17import { type LitTable, RedrawTreeForm } from '../../../../../base-ui/table/lit-table'; 18import { type SelectionParam } from '../../../../bean/BoxSelection'; 19import { getGpufreqData } from '../../../../database/sql/Perf.sql'; 20import { resizeObserver } from '../SheetUtils'; 21import { type GpuCountBean, TreeDataBean, TreeDataStringBean } from '../../../../bean/GpufreqBean'; 22 23@element('tabpane-gpufreq') 24export class TabPaneGpufreq extends BaseElement { 25 private threadStatesTbl: LitTable | null | undefined; 26 private currentSelectionParam: SelectionParam | undefined; 27 private SUB_LENGTH: number = 3; 28 private PERCENT_SUB_LENGTH: number = 2; 29 private UNIT: number = 1000000; 30 private KUNIT: number = 1000000000000; 31 32 set data(clockCounterValue: SelectionParam) { 33 let finalGpufreqData: Array<TreeDataStringBean> = new Array(); 34 if (this.currentSelectionParam === clockCounterValue) { 35 return; 36 } 37 this.currentSelectionParam = clockCounterValue; 38 this.threadStatesTbl!.recycleDataSource = []; 39 this.threadStatesTbl!.loading = true; 40 getGpufreqData(clockCounterValue.leftNs, clockCounterValue.rightNs, false).then( 41 (result: Array<GpuCountBean>): void => { 42 if (result !== null && result.length > 0) { 43 let resultList: Array<GpuCountBean> = JSON.parse(JSON.stringify(result)); 44 resultList[0].dur = resultList[1] 45 ? resultList[1].startNS - clockCounterValue.leftNs 46 : clockCounterValue.rightNs - clockCounterValue.leftNs; 47 resultList[0].value = resultList[0].dur * resultList[0].val; 48 resultList[resultList.length - 1].dur = 49 resultList.length - 1 !== 0 50 ? clockCounterValue.rightNs - resultList[resultList.length - 1].startNS 51 : resultList[0].dur; 52 resultList[resultList.length - 1].value = 53 resultList.length - 1 !== 0 54 ? resultList[resultList.length - 1].dur * resultList[resultList.length - 1].val 55 : resultList[0].value; 56 // 将切割完成后的数据整理成树形 57 let tree: TreeDataStringBean = this.createTree(resultList); 58 finalGpufreqData.push(tree); 59 this.threadStatesTbl!.recycleDataSource = finalGpufreqData; 60 this.threadStatesTbl!.loading = false; 61 this.clickTableHeader(finalGpufreqData); 62 } else { 63 this.threadStatesTbl!.recycleDataSource = []; 64 this.threadStatesTbl!.loading = false; 65 } 66 } 67 ); 68 } 69 70 initElements(): void { 71 this.threadStatesTbl = this.shadowRoot?.querySelector<LitTable>('#tb-gpufreq-percent'); 72 } 73 74 // 创建树形结构 75 private createTree(data: Array<GpuCountBean>): TreeDataStringBean { 76 if (data.length > 0) { 77 const root: { 78 thread: string; 79 value: number; 80 dur: number; 81 percent: number; 82 children: TreeDataBean[]; 83 } = { 84 thread: 'gpufreq Frequency', 85 value: 0, 86 dur: 0, 87 percent: 100, 88 children: [], 89 }; 90 const valueMap: { [freq: string]: TreeDataBean } = {}; 91 data.forEach((item: GpuCountBean) => { 92 let freq: number = item.freq; 93 item.thread = `${item.thread} Frequency`; 94 this.updateValueMap(item, freq, valueMap); 95 }); 96 Object.values(valueMap).forEach((node: TreeDataBean) => { 97 const parentNode: TreeDataBean = valueMap[node.freq! - 1]; 98 if (parentNode) { 99 parentNode.children.push(node); 100 parentNode.dur += node.dur; 101 parentNode.value += node.value; 102 } else { 103 root.children.push(node); 104 root.dur += node.dur; 105 root.value += node.value; 106 } 107 }); 108 this.flattenAndCalculate(root, root); 109 let _root = this.RetainDecimals(root); 110 return _root; 111 } 112 return new TreeDataStringBean('', '', '', '', '', ''); 113 } 114 // 更新valueMap,用于整理相同频点的数据 115 private updateValueMap(item: GpuCountBean, freq: number, valueMap: { [freq: string]: TreeDataBean }): void { 116 if (!valueMap[freq]) { 117 valueMap[freq] = { 118 thread: 'gpufreq Frequency', 119 value: item.value, 120 freq: item.freq, 121 dur: item.dur, 122 percent: 100, 123 children: [], 124 }; 125 } else { 126 valueMap[freq].dur += item.dur; 127 valueMap[freq].value += item.value; 128 } 129 valueMap[freq].children.push(item as unknown as TreeDataBean); 130 } 131 // 对树进行扁平化处理和计算 132 private flattenAndCalculate(node: TreeDataBean, root: TreeDataBean): void { 133 //处理百分比计算问题并保留两位小数 134 node.percent = (node.value / root.value) * 100; 135 if (node.children) { 136 node.children = node.children.flat(); 137 node.children.forEach((childNode) => this.flattenAndCalculate(childNode, root)); 138 } 139 } 140 // 将树形数据进行保留小数操作 141 private RetainDecimals(root: TreeDataBean): TreeDataStringBean { 142 const treeDataString: TreeDataStringBean = new TreeDataStringBean( 143 root.thread!, 144 (root.value / this.KUNIT).toFixed(this.SUB_LENGTH), 145 (root.dur / this.UNIT).toFixed(this.SUB_LENGTH), 146 root.percent!.toFixed(this.PERCENT_SUB_LENGTH), 147 String(root.level), 148 '', 149 0, 150 [], 151 '', 152 false 153 ); 154 if (root.children) { 155 for (const child of root.children) { 156 treeDataString.children!.push(this.convertChildToString(child) as TreeDataStringBean); 157 } 158 } 159 return treeDataString; 160 } 161 // 将树形数据进行保留小数的具体操作 162 private convertChildToString(child: TreeDataBean | TreeDataBean[]): TreeDataStringBean | TreeDataStringBean[] { 163 if (Array.isArray(child)) { 164 if (child.length > 0) { 165 return child.map((c) => this.convertChildToString(c) as TreeDataStringBean); 166 } else { 167 return []; 168 } 169 } else if (child && child.children) { 170 return { 171 thread: child.thread as string, 172 value: (child.value / this.KUNIT).toFixed(this.SUB_LENGTH), 173 freq: '', 174 dur: (child.dur / this.UNIT).toFixed(this.SUB_LENGTH), 175 percent: child.percent ? child.percent.toFixed(this.PERCENT_SUB_LENGTH) : '', 176 children: this.convertChildToString(child.children) as unknown as TreeDataStringBean[], 177 }; 178 } else { 179 return { 180 thread: child.thread as string, 181 value: (child.value / this.KUNIT).toFixed(this.SUB_LENGTH), 182 freq: child.freq ? child.freq!.toFixed(this.SUB_LENGTH) : '', 183 dur: (child.dur / this.UNIT).toFixed(this.SUB_LENGTH), 184 percent: child.percent ? child.percent.toFixed(this.PERCENT_SUB_LENGTH) : '', 185 level: String(child.level), 186 }; 187 } 188 } 189 // 表头点击事件 190 private clickTableHeader(data: Array<TreeDataStringBean>): void { 191 let labels = this.threadStatesTbl?.shadowRoot?.querySelector('.th > .td')!.querySelectorAll('label'); 192 const THREAD_INDEX: number = 0; 193 const FREQ_INDEX: number = 1; 194 195 if (labels) { 196 for (let i = 0; i < labels.length; i++) { 197 let label = labels[i].innerHTML; 198 labels[i].addEventListener('click', (e) => { 199 if (label.includes('Thread') && i === THREAD_INDEX) { 200 this.threadStatesTbl!.setStatus(data, false); 201 this.threadStatesTbl!.recycleDs = this.threadStatesTbl!.meauseTreeRowElement(data, RedrawTreeForm.Retract); 202 } else if (label.includes('Freq') && i === FREQ_INDEX) { 203 for (let item of data) { 204 item.status = true; 205 if (item.children !== undefined && item.children.length > 0) { 206 this.threadStatesTbl!.setStatus(item.children, true); 207 } 208 } 209 this.threadStatesTbl!.recycleDs = this.threadStatesTbl!.meauseTreeRowElement(data, RedrawTreeForm.Expand); 210 } 211 }); 212 } 213 } 214 } 215 connectedCallback(): void { 216 super.connectedCallback(); 217 resizeObserver(this.parentElement!, this.threadStatesTbl!); 218 } 219 220 initHtml(): string { 221 return ` 222 <style> 223 :host{ 224 padding: 10px 10px; 225 display: flex; 226 flex-direction: column; 227 } 228 </style> 229 <lit-table id="tb-gpufreq-percent" style="height: auto; overflow-x:auto;width:calc(100vw - 270px)" tree> 230 <lit-table-column class="gpufreq-percent-column" width='25%' title="Thread/Freq" data-index="thread" key="thread" align="flex-start" retract> 231 </lit-table-column> 232 <lit-table-column class="gpufreq-percent-column" width='1fr' title="consumption(MHz·ms)" data-index="value" key="value" align="flex-start"> 233 </lit-table-column> 234 <lit-table-column class="gpufreq-percent-column" width='1fr' title="Freq(MHz)" data-index="freq" key="freq" align="flex-start"> 235 </lit-table-column> 236 <lit-table-column class="gpufreq-percent-column" width='1fr' title="dur(ms)" data-index="dur" key="dur" align="flex-start"> 237 </lit-table-column> 238 <lit-table-column class="gpufreq-percent-column" width='1fr' title="Percent(%)" data-index="percent" key="percent" align="flex-start"> 239 </lit-table-column> 240 </lit-table> 241 `; 242 } 243} 244