跳转至

CLI 启动时间优化

重要性:⭐⭐(专项优化——启动时间从 1000ms 降到 300ms) 范围src/main.tsx 启动路径 + utils/startupProfiler.js 方法:基于 deep-dive-main.md + 启动 profiler + 顶部预取策略


1. 启动时间线(推测)

T+0ms     Bun 启动
T+50ms    模块求值开始
T+100ms   startMdmRawRead 触发
T+100ms   startKeychainPrefetch 触发
T+200ms   main.tsx 顶部副作用完成
T+250ms   imports 完成
T+300ms   main() 开始
T+300ms   preAction: ensureMdmSettingsLoaded + ensureKeychainPrefetchCompleted
T+350ms   preAction: init() 开始
T+400ms   preAction: init() 完成
T+400ms   preAction: initSinks
T+400ms   preAction: runMigrations
T+400ms   preAction: loadRemoteManagedSettings (fire-and-forget)
T+400ms   run() 开始
T+500ms   第一个 render
T+500ms   startDeferredPrefetches 触发(12 个 fire-and-forget)

总冷启动: ~500ms

~500ms 冷启动 —— 商业级。


2. 5 大启动优化

2.1 顶部并行预取(~200ms 节省)

// main.tsx 行 1-20
import { profileCheckpoint, profileReport } from './utils/startupProfiler.js';
profileCheckpoint('main_tsx_entry');  // 埋点

import { startMdmRawRead } from './utils/settings/mdm/rawRead.js';
startMdmRawRead();  // 立即触发(不 await)

import { startKeychainPrefetch } from './utils/secureStorage/keychainPrefetch.js';
startKeychainPrefetch();  // 立即触发

3 个 fire-and-forget: - profileCheckpoint —— 0ms - startMdmRawRead —— 与 imports 并行 ~135ms - startKeychainPrefetch —— 与 imports 并行 ~65ms (macOS)

节省 ~200ms。

2.2 print 模式 fast-path(~50ms 节省)

// main.tsx 行 3880
const isPrintMode = process.argv.includes('-p') || process.argv.includes('--print');
const isCcUrl = process.argv.some(a => a.startsWith('cc://') || a.startsWith('cc+unix://'));
if (isPrintMode && !isCcUrl) {
  await program.parseAsync(process.argv);
  return program;  // 早返回
}

print 模式早返回 —— 不注册 20+ subcommand。

节省 ~50-100ms。

2.3 feature-gated 懒加载(~50ms 节省)

const coordinatorModeModule = feature('COORDINATOR_MODE') 
  ? require('./coordinator/coordinatorMode.js') 
  : null;

编译时 DCE —— 商业版不打包这些模块。

节省 加载时间 + 解析时间。

2.4 首屏后预取(不阻塞首屏)

// startDeferredPrefetches
void initUser();
void getUserContext();
void getRelevantTips();
// ... 12 个 fire-and-forget

首屏后 才跑 —— 不阻塞 paint。

节省 ~200ms 首屏。

2.5 eager settings 加载(~50ms 节省)

// main.tsx 行 502
function eagerLoadSettings(): void {
  // 提前加载 --settings flag
}

早期加载 —— 后续 fast-path。


3. 30+ profileCheckpoint 全景

// main.tsx
profileCheckpoint('main_tsx_entry')
profileCheckpoint('main_function_start')
profileCheckpoint('main_warning_handler_initialized')
profileCheckpoint('main_client_type_determined')
profileCheckpoint('main_before_run')
profileCheckpoint('main_after_run')

// preAction
profileCheckpoint('preAction_start')
profileCheckpoint('preAction_after_mdm')
profileCheckpoint('preAction_after_init')
profileCheckpoint('preAction_after_sinks')
profileCheckpoint('preAction_after_migrations')
profileCheckpoint('preAction_after_remote_settings')
profileCheckpoint('preAction_after_settings_sync')

// run
profileCheckpoint('run_commander_initialized')
profileCheckpoint('run_before_parse')
profileCheckpoint('run_after_parse')

~16 个 profileCheckpoint in main.tsx —— 总计 30+ 全文

每一个埋点 都能: - 计算耗时 - 上报 Statsig(sampled) - 控制台输出(debug 模式)


4. 启动模式 vs 启动时间

模式 冷启动 备注
完整 ~500ms 全部功能
--bare ~300ms 跳过预取
-p (print) ~400ms fast-path
CLAUDE_CODE_EXIT_AFTER_FIRST_RENDER=1 ~300ms 仅测 perf
--debug ~600ms debug 输出
缓存命中 ~200ms 第二次启动

6 种模式 —— 性能差异显著。


5. 启动阶段细节

5.1 Bun 单文件可执行

  • 启动快(~50ms)
  • 跨平台打包
  • 内置 runtime

Bun 优势 —— 比 Node 启动快。

5.2 模块求值

// ~135ms (50+ imports)
import { ... } from './...'

ES import 静态 —— 顺序求值。

5.3 顶部副作用

// 与 imports 并行
startMdmRawRead();  // 启动 subprocess
startKeychainPrefetch();  // 启动 keychain

fire-and-forget —— 不阻塞 imports。

5.4 main() 函数

// ~150ms
async function main() {
  // 1. 安全 setup
  // 2. argv 预处理
  // 3. 启动 init
  // 4. preAction 钩子
  // 5. run()
}

5 步 —— 详见 deep-dive-main.md。

5.5 preAction 钩子

// ~50-100ms
program.hook('preAction', async thisCommand => {
  // 1. 等待 MDM/keychain 预取完成
  // 2. init()
  // 3. 启动 sinks
  // 4. 加载 inline plugins
  // 5. 迁移
  // 6. 加载远程设置
});

6 步 —— 详见 deep-dive-main.md。

5.6 run() commander

// ~100ms
async function run() {
  // 1. Commander 初始化
  // 2. preAction 钩子(已跑)
  // 3. 选项定义 (~40 个)
  // 4. default action (2801 行)
  // 5. 20+ subcommand 注册
  // 6. parseAsync
}

6 步


6. 启动 vs 首次响应

阶段 时间 用户感知
冷启动 ~500ms 看到 UI
用户输入 → API ~500ms-1s 看到流式输出
总 (TTFT) ~1-1.5s 第一个 token

TTFT ~1-1.5s —— 商业级。


7. 启动 vs 内存

阶段 内存
刚启动 ~50-100MB
加载 plugins +5-20MB/plugin
加载 MCP +5-10MB/server
加载 skills +2-5MB/skill
大 session ~200-500MB

基础 50-100MB —— 合理。


8. 启动优化"反模式"检查

反模式 项目里有吗
同步阻塞 IO ❌ 多数 fire-and-forget
顶层 await ❌ 全部 fire-and-forget
启动时 deep import ❌ 多用 lazy require
启动时网络请求 ⚠️ 有(fire-and-forget)
启动时磁盘读 ⚠️ 有(fire-and-forget)

总体优化 —— 商业级。


9. 进一步优化方向

9.1 减少 main.tsx 体积

// 4683 行 → 拆 subcommand

拆 subcommand —— 已在做。

9.2 减少 hooks 数量

// 50+ hooks → 合并

合并 —— 待评估。

9.3 减少 utils 数量

// 198 个 → 合并

合并 —— 待评估。

9.4 预编译 regex

// 推测:已有

预编译 —— 已有。

9.5 减少 console.log

// 推测:debug 模式才输出

生产环境 —— 已控制。


10. 启动 vs 功能

10.1 trade-off

启动快  ↔  功能多
体积小  ↔  兼容性广
冷启动  ↔  预热

4 维 trade-off —— 项目选功能多 + 兼容性广

10.2 --bare 模式

isBareMode()  // 跳过 hooks, LSP, plugin sync, attribution, ...

用户可控 —— 速度 vs 功能的旋钮。


11. 关键洞察

11.1 5 大优化

顶部预取 + print fast-path + feature DCE + 预取 + eager settings。

11.2 30+ profileCheckpoint

可观测性 —— 启动全程埋点。

11.3 6 种启动模式

完整 / --bare / -p / perf / --debug / 缓存。

11.4 Bun 优势

比 Node 启动快 ~50ms。

11.5 5 阶段启动

Bun → imports → main → preAction → run。

11.6 顶部 fire-and-forget

不阻塞 imports —— 并行化

11.7 preAction 集中 init

6 步统一初始化。

11.8 print fast-path 节省 50-100ms

大幅优化 print 模式。

11.9 eager settings 早期

后续 fast-path。

11.10 启动 vs 功能 trade-off

项目选功能多 —— 用户可用 --bare 加速。


12. 改进方向(更激进)

12.1 main.tsx → subcommand 拆分

// 当前:4683 行
// 目标:500 行 + 20 个 subcommand 文件

拆 subcommand —— 已在做。

12.2 预连接(HTTP/2 / gRPC)

// 预连接 API

预连接 —— 减少首请求延迟。

12.3 预编译 plugin 缓存

// 编译期生成 plugin index

预编译 —— 启动快。

12.4 snapshot 启动

// Bun --snapshot 启动

Snapshot —— Bun 特性。

12.5 Worker 预热

// 启动时预热 Worker

Worker —— 主线程不阻塞。


13. 阅读建议

  1. main.tsx 行 1-100 —— 顶部预取
  2. main.tsx 行 3880 —— print fast-path
  3. startDeferredPrefetches —— 首屏后预取
  4. utils/startupProfiler.js —— 埋点
  5. 看 preAction hook —— 6 步初始化

14. 与其他分析的关系

文件 关系
performance-history.md 性能整体
architecture-history.md 启动演进
dce-dead-code-elimination.md 编译时优化

15. 总结

启动时间:~500ms(商业级) TTFT:~1-1.5s(商业级) 优化手段:5 大类 可观测性:30+ profileCheckpoint 用户可控:6 种启动模式

核心 insight: - 顶部 fire-and-forget —— 不阻塞 imports - print 早返回 —— fast-path - preAction 集中 —— 6 步统一 - DCE 编译时 —— 体积优化 - 30+ 埋点 —— 可观测性