| Files | Last commit | Last update |
|---|---|---|
| 3 months ago | ||
| 3 months ago | ||
| 3 months ago | ||
| 4 months ago | ||
| 4 months ago |
@cptf/benchmark
CPTF benchmark 插件 —— host 侧(Node.js 测试运行器内)。提供 renderTime / unmountTime 两个指标的采集器,桥接设备侧 WebSocket 事件到 CPTF 框架的 MetricCollectorFactory。
与
@cptf/benchmark-rn(RN 设备侧)互为兄弟;@cptf/benchmark-rn 依赖@cptf/benchmark/metric-types导入指标类型常量。本包不含任何react-native依赖,可被任意平台的 CPTF 用例复用。
指标定义
本包提供的指标
| 指标 | 类型常量 | 含义 | 采集方式 |
|---|---|---|---|
renderTime |
RENDER_TIME_METRIC_TYPE = 'renderTime' |
从点击渲染按钮到首批子组件首帧绘制完成的时间(ms) | 订阅 WebSocket 事件;回退读 benchmark-render-time 元素文本 |
unmountTime |
UNMOUNT_TIME_METRIC_TYPE = 'unmountTime' |
从点击重置按钮到 MeasureContainer 及子组件完全从原生视图树移除的时间(ms) | 订阅 WebSocket 事件;回退读 benchmark-unmount-time 元素文本 |
采集链路:设备侧原生组件(onDraw / onDetachedFromWindow)测量 → WebSocket 上报 → MetricWebSocketServer → global.__metricEventBus → EventBusMetricBridge → 本包 collector 订阅 → ExecutionEngine.collect() → 报告。
回退机制:当 WebSocket 事件未到达时,renderTime collector 用 waitForTestId 轮询 15s 等待 benchmark-render-time 元素出现;unmountTime collector 用 findByTestId 单次查找 benchmark-unmount-time 元素(非轮询)。
框架内置指标(非本包提供)
以下指标由 @cptf/framework 的 PerformanceCollector 通过系统命令采集,与本包无关:
| 指标 | 采集来源 | 说明 |
|---|---|---|
fps |
HarmonyOS: hidumper -s RenderService / Android: dumpsys gfxinfo |
渲染帧率 |
memoryPeak |
HarmonyOS: hidumper --mem <PID> / Android: dumpsys meminfo <PID> |
进程内存峰值 |
blankScreenRatio |
ScreenRecorder 录屏 → 逐帧分析白屏比例 |
白屏率 |
用例中
collect: ['renderTime', 'unmountTime', 'fps', 'memoryPeak']前两个由本包提供,后两个由框架内置。
模块
| 文件 | 职责 |
|---|---|
src/metric-types.ts |
指标类型常量:RENDER_TIME_METRIC_TYPE = 'renderTime'、UNMOUNT_TIME_METRIC_TYPE = 'unmountTime'。两端(本包 + 设备侧 @cptf/benchmark-rn)共用。 |
src/metric-bridge.ts |
MetricBridge 契约:subscribe(eventName, onValue, onError?) => Unsubscribe。平台无关的事件源抽象。 |
src/event-bus-metric-bridge.ts |
EventBusMetricBridge:包装 global.__metricEventBus(CPTF 的 WS server 已把 metric 事件喂入该 bus)。默认桥实现。 |
src/preset-collectors/render-time-collector.ts |
RenderTimeCollector:onStart 订阅 bridge 'renderTime';collect 先检查 WS 事件值,回退读 benchmark-render-time 文本 /Took (\d+)ms/。 |
src/preset-collectors/unmount-time-collector.ts |
UnmountTimeCollector:onStart 订阅 bridge 'unmountTime';collect 先检查 WS 事件值,回退读 benchmark-unmount-time 文本(findByTestId 单次查找,非轮询)。 |
src/index.ts |
registerBenchmarkMetrics(bridge?):用 bridge 构造 collector 并注册到 MetricCollectorFactory。底部自动调用(默认 EventBusMetricBridge),故 import '@cptf/benchmark' 即完成注册。 |
工作流程
设备 app (@cptf/benchmark-rn device-metric-provider)
└─ WS send {type:'renderTime', value, markerName, deviceId} ─→
宿主 @cptf/framework MetricWebSocketServer (acquireMetricServer 全局单例, port 18081)
└─ emit global.__metricEventBus
└→ 本包 EventBusMetricBridge 订阅
└→ RenderTimeCollector / UnmountTimeCollector
└→ MetricCollectorFactory.register (覆盖框架 stub)
└→ ExecutionEngine.collect() → TestResult.extraMetrics → 报告
事件格式:
{
"type": "renderTime",
"value": 437,
"markerName": "views1500",
"timestamp": 1783677892000,
"deviceId": "7NXBB26324000068"
}
deviceId 过滤:collector 声明了 deviceId 字段用于多设备过滤,但当前实现未在 onStart 中赋值(this.deviceId 始终为 null),过滤逻辑实际为空操作。多设备场景需后续补充赋值逻辑。
用法
// app/tests/cases/performance/views-1500-render-perf.test.ts
import 'reflect-metadata';
import { Test, Step, PerformanceTest } from '@cptf/framework';
import '@cptf/benchmark'; // 副作用:registerBenchmarkMetrics() 自动注册
@Test({
id: 'views-1500-render-perf',
scene: 'views-1500',
collect: ['renderTime', 'unmountTime', 'fps', 'memoryPeak'],
// ↑ 本包提供 ↑ 框架内置
})
class Views1500RenderPerfTest extends PerformanceTest { ... }
本包是 built dist(
main: dist/index.js)。CPTF 运行器用ts-node(transpileOnly,忽略 node_modules 内的 .ts),host 侧消费必须走编译产物——改源码后执行pnpm run build。
扩展指南
1. 新增一个指标类型(host 侧 collector)
例:增加一个 firstPaint 指标。
- 在
src/metric-types.ts加常量:export const FIRST_PAINT_METRIC_TYPE = 'firstPaint' as const; - 在
src/preset-collectors/加first-paint-collector.ts:import { BaseMetricCollector, MetricCollectorContext } from '@cptf/framework'; import { MetricBridge, MetricEvent, Unsubscribe } from '../metric-bridge'; import { FIRST_PAINT_METRIC_TYPE } from '../metric-types'; export class FirstPaintCollector extends BaseMetricCollector { type = FIRST_PAINT_METRIC_TYPE; private value = 0; private unsub: Unsubscribe | null = null; constructor(private bridge: MetricBridge) { super(); } async onStart(ctx: MetricCollectorContext) { this.unsub = this.bridge.subscribe<MetricEvent>('firstPaint', e => { if (e && typeof e.value === 'number') this.value = e.value; }); } async onFinish(_ctx: MetricCollectorContext) { this.unsub?.(); this.unsub = null; } async collect(ctx: MetricCollectorContext) { return this.value; } } - 在
src/index.ts的registerBenchmarkMetrics里追加注册 + re-export 常量。 - 设备侧
deviceMetricProvider.report('firstPaint', value, markerName)。 - 用例
collect: ['firstPaint']。
2. 新增一个平台 bridge
MetricBridge 是平台无关契约。默认 EventBusMetricBridge(WS → __metricEventBus)。若你的平台用别的事件源,实现 MetricBridge:
import { MetricBridge, MetricEvent, Unsubscribe } from '@cptf/benchmark';
class MyBridge implements MetricBridge {
subscribe<T>(eventName: string, onValue: (v: T) => void, onError?: (e: Error) => void): Unsubscribe {
const off = myPlatform.on(eventName, (e: MetricEvent) => { try { onValue(e as T); } catch (err) { onError?.(err as Error); } });
return () => off();
}
}
registerBenchmarkMetrics(new MyBridge());
设计原则
- 只管 renderTime / unmountTime:fps、memoryPeak、blankScreenRatio 等指标由
@cptf/framework的PerformanceCollector/ScreenRecorder采集,与本包无关。 - host/device 分离:本包只跑在宿主 Node 进程;不 import
react-native;不进设备 bundle。 - 零侵入核心:不改
@cptf/framework,纯靠MetricCollectorFactory.register扩展。 - 事件驱动 collector:collector 不主动计时,只订阅事件——真实测量由设备侧原生完成,本包只搬运 + 回退兜底。