From 9b06ee20f6e38664dc91d63ed9e7f0e2dc237735 Mon Sep 17 00:00:00 2001 From: luobohua Date: Fri, 18 Jul 2025 10:44:12 +0800 Subject: [PATCH] build-system: Add time and memory analysis Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICMX5A Signed-off-by: luobohua --- ets2panda/driver/build_system/README.md | 10 +- .../build_system/src/build/base_mode.ts | 33 ++++- ets2panda/driver/build_system/src/types.ts | 6 + .../build_system/src/utils/record_time_mem.ts | 122 ++++++++++++++++++ 4 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 ets2panda/driver/build_system/src/utils/record_time_mem.ts diff --git a/ets2panda/driver/build_system/README.md b/ets2panda/driver/build_system/README.md index 414e8efc04..caa73c210a 100644 --- a/ets2panda/driver/build_system/README.md +++ b/ets2panda/driver/build_system/README.md @@ -65,4 +65,12 @@ To run tests: ```bash npm run ut_test npm run plugin_test -``` \ No newline at end of file +``` + +## Performance Analysis +The switch of performance analysis is located in the file arkcompiler/ets_frontend/ets2panda/driver/build_system/src/utils/record_time_mem.ts +To open the switch, change recordType to ON_TYPE. +``` +this.recordType = recordType ?? RECORD_TYPE.ON_TYPE +``` +And the report, named bs_record_perf.csv, is located in the project root directory. \ No newline at end of file diff --git a/ets2panda/driver/build_system/src/build/base_mode.ts b/ets2panda/driver/build_system/src/build/base_mode.ts index b2cc884429..2f48964c1f 100644 --- a/ets2panda/driver/build_system/src/build/base_mode.ts +++ b/ets2panda/driver/build_system/src/build/base_mode.ts @@ -71,6 +71,13 @@ import { import { ArkTSConfigGenerator } from './generate_arktsconfig'; import { SetupClusterOptions } from '../types'; import { KitImportTransformer } from '../plugins/KitImportTransformer'; +import { + BS_PERF_FILE_NAME, + CompileSingleData, + RECORDE_COMPILE_NODE, + RECORDE_MODULE_NODE, + RECORDE_RUN_NODE +} from '../utils/record_time_mem'; export abstract class BaseMode { public buildConfig: BuildConfig; @@ -303,7 +310,9 @@ export abstract class BaseMode { } } - public compileMultiFiles(filePaths: string[], moduleInfo: ModuleInfo): void { + public compileMultiFiles(moduleInfo: ModuleInfo): void { + let compileSingleData = new CompileSingleData(path.join(path.resolve(), BS_PERF_FILE_NAME)); + compileSingleData.record(RECORDE_COMPILE_NODE.PROCEED_PARSE); let ets2pandaCmd: string[] = [ '_', '--extension', @@ -332,21 +341,27 @@ export abstract class BaseMode { arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED, arktsGlobal.compilerContext.peer); this.logger.printInfo('es2panda proceedToState parsed'); + compileSingleData.record(RECORDE_COMPILE_NODE.PLUGIN_PARSE, RECORDE_COMPILE_NODE.PROCEED_PARSE); + let ast = arkts.EtsScript.fromContext(); PluginDriver.getInstance().getPluginContext().setArkTSAst(ast); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); this.logger.printInfo('plugin parsed finished'); + compileSingleData.record(RECORDE_COMPILE_NODE.PROCEED_CHECK, RECORDE_COMPILE_NODE.PLUGIN_PARSE); arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, arktsGlobal.compilerContext.peer); this.logger.printInfo('es2panda proceedToState checked'); + compileSingleData.record(RECORDE_COMPILE_NODE.PLUGIN_CHECK, RECORDE_COMPILE_NODE.PROCEED_CHECK); ast = arkts.EtsScript.fromContext(); PluginDriver.getInstance().getPluginContext().setArkTSAst(ast); PluginDriver.getInstance().runPluginHook(PluginHook.CHECKED); this.logger.printInfo('plugin checked finished'); + compileSingleData.record(RECORDE_COMPILE_NODE.BIN_GENERATE, RECORDE_COMPILE_NODE.PLUGIN_CHECK); arkts.proceedToState(arkts.Es2pandaContextState.ES2PANDA_STATE_BIN_GENERATED, arktsGlobal.compilerContext.peer); this.logger.printInfo('es2panda bin generated'); + compileSingleData.record(RECORDE_COMPILE_NODE.CFG_DESTROY, RECORDE_COMPILE_NODE.BIN_GENERATE); } catch (error) { errorStatus = true; throw error; @@ -357,6 +372,8 @@ export abstract class BaseMode { } PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); arkts.destroyConfig(arktsGlobal.config); + compileSingleData.record(RECORDE_COMPILE_NODE.END, RECORDE_COMPILE_NODE.CFG_DESTROY); + compileSingleData.writeSumSingle(path.resolve()); } } @@ -760,10 +777,17 @@ export abstract class BaseMode { } protected generateModuleInfos(): void { + let compileSingleData = new CompileSingleData(path.join(path.resolve(), BS_PERF_FILE_NAME)); + compileSingleData.record(RECORDE_MODULE_NODE.COLLECT_INFO); this.collectModuleInfos(); + compileSingleData.record(RECORDE_MODULE_NODE.GEN_CONFIG, RECORDE_MODULE_NODE.COLLECT_INFO); this.generateArkTSConfigForModules(); + compileSingleData.record(RECORDE_MODULE_NODE.CLT_FILES, RECORDE_MODULE_NODE.GEN_CONFIG); this.collectCompileFiles(); + compileSingleData.record(RECORDE_MODULE_NODE.SAVE_CACHE, RECORDE_MODULE_NODE.CLT_FILES); this.saveHashCache(); + compileSingleData.record(RECORDE_MODULE_NODE.END, RECORDE_MODULE_NODE.SAVE_CACHE); + compileSingleData.writeSumSingle(path.resolve()); } public async generateDeclaration(): Promise { @@ -780,6 +804,8 @@ export abstract class BaseMode { } public async run(): Promise { + let compileSingleData = new CompileSingleData(path.join(path.resolve(), BS_PERF_FILE_NAME)); + compileSingleData.record(RECORDE_RUN_NODE.GEN_MODULE); this.generateModuleInfos(); const compilePromises: Promise[] = []; @@ -790,9 +816,11 @@ export abstract class BaseMode { } moduleToFile.get(fileInfo.packageName)?.push(fileInfo.filePath); }); + compileSingleData.record(RECORDE_RUN_NODE.COMPILE_FILES, RECORDE_RUN_NODE.GEN_MODULE); try { //@ts-ignore - this.compileMultiFiles([], this.moduleInfos.get(this.packageName)); + this.compileMultiFiles(this.moduleInfos.get(this.packageName)); + compileSingleData.record(RECORDE_RUN_NODE.END, RECORDE_RUN_NODE.COMPILE_FILES); } catch (error) { if (error instanceof Error) { const logData: LogData = LogDataFactory.newInstance( @@ -803,6 +831,7 @@ export abstract class BaseMode { this.logger.printErrorAndExit(logData); } } + compileSingleData.writeSumSingle(path.resolve()); } // -- runParallell code begins -- diff --git a/ets2panda/driver/build_system/src/types.ts b/ets2panda/driver/build_system/src/types.ts index 80f3f76ab3..b223cd2d0c 100644 --- a/ets2panda/driver/build_system/src/types.ts +++ b/ets2panda/driver/build_system/src/types.ts @@ -13,6 +13,11 @@ * limitations under the License. */ +export enum RECORD_TYPE { + DEFAULT_TYPE = 'OFF', + ON_TYPE = 'ON', +} + export enum BUILD_MODE { DEBUG = 'Debug', RELEASE = 'Release' @@ -48,6 +53,7 @@ export interface BuildBaseConfig { arktsGlobal: ArkTSGlobal; maxWorkers?: number; isBuildConfigModified?: boolean; + recordType?: RECORD_TYPE; } export interface ArkTSGlobal { diff --git a/ets2panda/driver/build_system/src/utils/record_time_mem.ts b/ets2panda/driver/build_system/src/utils/record_time_mem.ts new file mode 100644 index 0000000000..683e2e9a9e --- /dev/null +++ b/ets2panda/driver/build_system/src/utils/record_time_mem.ts @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +import * as fs from 'fs'; +import path from 'path'; +import { RECORD_TYPE } from '../types' + +export const BS_PERF_FILE_NAME = 'bs_record_perf.csv' + +export enum RECORDE_RUN_NODE { + GEN_MODULE = 'run generateModuleInfos', + COMPILE_FILES = 'run compileMultiFiles', + END = 'run end', +} + +export enum RECORDE_COMPILE_NODE { + PROCEED_PARSE = 'compileMultiFiles proceedToState parsed', + PLUGIN_PARSE = 'compileMultiFiles plugin parsed', + PROCEED_CHECK = 'compileMultiFiles proceedToState checked', + PLUGIN_CHECK = 'compileMultiFiles plugin checked', + BIN_GENERATE = 'compileMultiFiles bin generated', + CFG_DESTROY = 'compileMultiFiles config destroyed', + END = 'compileMultiFiles end', +} + +export enum RECORDE_MODULE_NODE { + COLLECT_INFO = 'generateModuleInfos collectModuleInfos', + GEN_CONFIG = 'generateModuleInfos generateArkTSConfigForModules', + CLT_FILES = 'generateModuleInfos collectCompileFiles', + SAVE_CACHE = 'generateModuleInfos saveHashCache', + END = 'generateModuleInfos end', +} + +export class TimeMemData { + public startTime: number = 0; + public endtime: number = 0; + public time: number = 0; + public startMem: number = 0; + public endMem:number = 0; + public mem: number = 0; +} + +export class SingleData { + public time: number = 0; + public mem: number = 0; +} + +export class CompileSingleData { + private timeMemMap: Map; + private startTime: number = 0; + private startMem: number = 0; + private file: string = ''; + private recordType: RECORD_TYPE; + + constructor(file: string, recordType?: RECORD_TYPE) { + this.file = file; + this.timeMemMap = new Map(); + // close by default + this.recordType = recordType ?? RECORD_TYPE.DEFAULT_TYPE; + } + + public record(startKey: string, lastEndKey: string = '') { + if (this.recordType == RECORD_TYPE.DEFAULT_TYPE) { + return; + } + let currentTime = new Date().getTime(); + let currentMem = process.memoryUsage.rss(); + let tmp: SingleData | undefined = this.timeMemMap.get(lastEndKey); + if (tmp) { + tmp.time = currentTime - this.startTime; + tmp.mem = (currentMem > this.startMem) ? (currentMem - this.startMem) : 0; + this.timeMemMap.set(lastEndKey, tmp); + } + + if (startKey == '') { + return; + } + + let tmp1: SingleData | undefined = this.timeMemMap.get(startKey); + if (tmp1 == undefined) { + this.startTime = currentTime; + this.startMem = currentMem; + let data: SingleData = new SingleData(); + data.time = 0; + data.mem = 0; + this.timeMemMap.set(startKey, data); + } + } + + writeSumSingle(cachePath: string, deputyName: string = '') { + if (this.recordType == RECORD_TYPE.DEFAULT_TYPE) { + return; + } + const csvData: string[] = [ + "timeKey, time(ms), mem(M)" + ]; + this.timeMemMap.forEach((v: SingleData, k: string) => { + let element = `${k}` +', ' + `${v.time}` + 'ms' + ', ' + `${Math.round(v.mem / 1024 / 1024)}` + 'M' ; + csvData.push(element); + }); + let name = path.basename(this.file) + let currentExt = path.extname(name) + let fileWithoutExt = name.substring(0, name.lastIndexOf(currentExt)); + let fileName = `${fileWithoutExt}`+ deputyName +'.csv'; + let filePath = path.join(cachePath, fileName); + csvData.forEach(row => { + fs.appendFileSync(filePath, `${row}\n`); + }); + } +} \ No newline at end of file -- Gitee