Skip to content

Commit

Permalink
feat: enhance performance tracking and output (#34)
Browse files Browse the repository at this point in the history
* feat: enhance performance tracking and output

- Added performance measurement for TSC compilation, post-processing, and application startup phases.
- Introduced a new method `renderPerfStats` in `ConsoleOutput` to display performance statistics.
- Updated performance tracking in the `run` function to capture and log durations for various stages.
- Minor code cleanup and comments for clarity.

This update improves the observability of the build process and helps identify performance bottlenecks.

* feat: conditionally render performance stats based on init flag

- Added a check for the '--perf-init' argument to determine if performance statistics should be displayed.
- Updated the `run` function to conditionally call `renderPerfStats` for both initial and restart performance statistics based on the new flag.
- This enhancement improves the flexibility of performance tracking during the build process.
  • Loading branch information
czy88840616 authored Dec 2, 2024
1 parent 3979bf2 commit 6567afc
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 2 deletions.
86 changes: 85 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const chokidar = require('chokidar');
const fs = require('fs');
const { startProxyServer } = require('./proxy');
const consoleOutput = require('./output');
const { performance } = require('perf_hooks');

function run() {
const { cmdPath, tscArgs, parentArgs, childArgs } = parseArgs(process.argv);
Expand All @@ -35,7 +36,7 @@ function run() {
tscArgs.push(['--preserveWatchOutput']);
}

// 添加 --listEmittedFiles 参数以便于获取编译后的文件列表
// 添加 --listEmittedFiles 参数以便于获取编译后文件列表
if (
tscArgs.includes('--watch') &&
!tscArgs.includes('--listEmittedFiles') &&
Expand Down Expand Up @@ -131,6 +132,13 @@ function run() {
const restart = debounce(async fileChangedList => {
debug(`fileChangedList: ${fileChangedList}`);
if (fileChangedList && fileChangedList.length === 0) return;

performance.clearMarks();
performance.clearMeasures();

// 1. 进程关闭阶段
performance.mark('kill-start');

async function aliasReplace() {
if (hasPaths) {
// 这里使用全量替换,tsc 增量编译会把老文件修改回去
Expand All @@ -148,11 +156,19 @@ function run() {
}

await Promise.all([runChild && runChild.kill(), aliasReplace()]);
performance.mark('kill-end');
// 根据是否有 alias replace 来决定性能统计的名称
const killMeasureName = hasPaths ? 'Kill & Alias Replace' : 'Process Kill';
performance.measure(killMeasureName, 'kill-start', 'kill-end');

// 清理代理文件缓存
if (proxyServer) {
proxyServer.clearCache();
}
debug('Fork new child process');

// 3. 应用启动阶段
performance.mark('app-start');
runChild && runChild.forkChild();
}, 1000);

Expand Down Expand Up @@ -217,32 +233,100 @@ function run() {
}

// 启动执行 tsc 命令
const perfStats = [];
let restartPerfStats = [];

// 检查是否需要输出性能统计
const isPerfInit = childArgs.includes('--perf-init');

// 1. TSC 编译阶段
performance.mark('tsc-start');
const child = forkTsc(tscArgs, {
cwd,
onFirstWatchCompileSuccess: () => {
performance.mark('tsc-end');
performance.measure('TSC Compilation', 'tsc-start', 'tsc-end');
const tscMeasure = performance.getEntriesByName('TSC Compilation')[0];
perfStats.push({
name: 'TSC Compilation',
duration: tscMeasure.duration
});

// 2. 后处理阶段(alias 替换和文件拷贝)
performance.mark('post-start');
runAfterTsc();
performance.mark('post-end');
performance.measure('Post Processing', 'post-start', 'post-end');
const postMeasure = performance.getEntriesByName('Post Processing')[0];
perfStats.push({
name: hasPaths ? 'Alias Replace & Copy' : 'File Copy',
duration: postMeasure.duration
});

watchDeleteFile();

if (cmdPath) {
// 3. 应用启动阶段
performance.mark('app-start');
runChild = forkRun(cmdPath, childArgs, {
cwd,
parentArgs,
});

// 设置一次性的 onServerReady 回调
runChild.onServerReady(
async (serverReportOption, isFirstCallback, during, debugUrl) => {
if (isFirstCallback) {
performance.mark('app-end');
performance.measure('App Startup', 'app-start', 'app-end');
const appMeasure = performance.getEntriesByName('App Startup')[0];
perfStats.push({
name: 'App Startup',
duration: appMeasure.duration
});

// 第一次启动把端口等信息打印出来
consoleOutput.renderServerFirstReady(
serverReportOption,
during,
hasPaths,
debugUrl
);

if (isPerfInit) {
consoleOutput.renderPerfStats(perfStats);
}

if (proxyServer) {
proxyServer.start();
}
triggerMessage('server-first-ready');
} else {
// 重启完成后的处理
performance.mark('app-end');
performance.measure('App Restart', 'app-start', 'app-end');

const measures = performance.getEntriesByType('measure');
const killMeasureName = hasPaths ? 'Kill & Alias Replace' : 'Process Kill';
const killMeasure = measures.find(m => m.name === killMeasureName);
const appMeasure = measures.find(m => m.name === 'App Restart');

restartPerfStats = [
{
name: killMeasureName,
duration: killMeasure.duration
},
{
name: 'App Restart',
duration: appMeasure.duration
}
];

consoleOutput.renderServerReady(during, debugUrl);
if (isPerfInit) {
consoleOutput.renderPerfStats(restartPerfStats);
}

triggerMessage('server-ready');
}
}
Expand Down
15 changes: 15 additions & 0 deletions lib/output.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,24 @@ class ConsoleOutput extends EventEmitter {
}
}

console.log('\nMidway Performance Statistics:');
console.log(table.toString());
}

renderPerfStats(items) {
const table = new Table({
head: ['Stage', 'Time(ms)'],
});

for (const item of items) {
table.push([item.name, item.duration.toFixed(2)]);
}

console.log('\nPerformance Statistics:');
console.log(table.toString());
console.log('');
}

renderKeepAlive() {
output(colors.red('*'.repeat(120)));
output(
Expand Down
2 changes: 1 addition & 1 deletion lib/process.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ const forkRun = (runCmdPath, runArgs = [], options = {}) => {
} else if (data.title === 'debug-url') {
currentDebugUrl = data.debugUrl;
} else if (data.title === 'perf-init') {
if (isPerfInit && isFirstFork) {
if (isPerfInit) {
// perf init
consoleOutput.renderPerfInit(data.data);
}
Expand Down

0 comments on commit 6567afc

Please sign in to comment.