最近在做的 perfcat-windows,是一款面向 Windows 的进程性能监视器。它使用 Rust 采集本机数据,通过 Tauri 把能力交给 React 界面,再将 CPU、内存、GPU 显存、FPS、磁盘 I/O、网络、句柄和线程等指标放到同一条时间线上。

项目当前处于 0.1.0。功能还在继续完善,但实现过程中有几个问题很值得单独记录:来自不同系统接口的数据如何对齐、采集失败应当如何表达,以及怎样让一次意外退出不至于毁掉整段记录。
先确定数据的语义
性能监控最容易出现的错误,不一定是数值算错,而是把不同含义的数据画在了一起。
perfcat 中的各类采样器彼此独立:CPU 和内存来自进程相关的 Windows API,GPU 显存依赖性能计数器,网络使用 ETW 与 IP Helper,FPS 则可以来自 RTSS 或实验性的 ETW Present。它们的启动条件、权限要求和返回节奏都不相同。
因此,前端不会直接接收一组没有关联的散乱事件。核心层先用 FrameAssembler 按采样序号聚合数据,再输出版本化的 SampleFrame:
Windows 数据源 ↓独立 Sampler ↓MetricSample(sequence, kind, value) ↓FrameAssembler ↓SampleFrame + FrameQuality ↓Tauri 事件 / JSONL 会话 / React 图表一个 SampleFrame 除了指标值,还包含会话 ID、序号、采集时间、目标进程、采样间隔和 schema 版本。FrameQuality 则明确记录这一帧是否完整,以及缺失了哪些指标。这样一来,UI、存储和之后可能出现的导出工具都在消费同一种数据语义。
“没有采到”不等于零
这是我在这个项目里最想坚持的一条规则。
假设 RTSS 没有挂接目标进程,或者某个采样周期中没有新的 Present 事件,此时把 FPS 写成 0 看似方便,实际上会制造一条错误结论:程序真的运行到了 0 FPS。更准确的表达应该是“这个时间点没有可信测量结果”。
所以 perfcat 会把对应指标保留为空,并在帧质量中标记缺失。图表上呈现的是间隙,界面也会告诉用户不可用的原因。其他正常工作的采样器仍然继续运行,而不是因为一个指标失败就结束整个会话。
这种设计不只适用于 FPS。网络 ETW 可能缺少管理员权限,GPU 性能计数器也可能没有目标进程实例。与其用整齐但虚假的曲线掩盖问题,不如忠实保留数据的不确定性。
两种 FPS 来源,两种测量含义
当前默认方案是读取 RTSS 共享内存。这条路径成熟、结果直观,但要求本机已经安装并运行 RTSS,并且它成功挂接目标图形进程。
另一个实验选项是 ETW Present。perfcat 会启动独立命名的实时 ETW 会话,筛选目标 PID 的 DXGI/D3D9 Present 起始事件,并保留原始 QPC 时间戳。它不依赖 RTSS,但通常需要管理员权限,而且测到的是应用提交 Present 的节奏,不应直接等同于画面最终显示到屏幕的帧率。
既然语义不同,FPS 来源在一次会话开始后就会锁定。否则同一条曲线的前半段和后半段可能来自两套口径,数值看起来连续,实际却无法比较。
帧时间除了平均值,还会计算 P95、标准差、卡顿次数、大卡顿次数和卡顿占比。平均值可以描述总体速度,但定位偶发卡顿时,尾部指标通常更有价值。
为什么会话存成追加式 JSONL
每次开始监控,应用都会在 %APPDATA%\perfcat\sessions 中创建一个会话文件。文件按行依次写入:
- 会话头,保存目标进程、指标配置和开始时间;
- 一个个完整的
SampleFrame; - 正常停止时写入结束标记。
每写入一帧就刷新到磁盘。应用如果异常退出,之前已经完成的行仍然可以读取;解析时会忽略损坏的尾行,没有结束标记的会话则显示为“异常中断”。
这里没有引入数据库。对于一条主要按时间追加、很少随机更新的性能会话,JSONL 足够透明,也方便排查和迁移。以后若增加索引、复杂查询或跨会话统计,再评估更重的存储方案会更合适。
Rust、Tauri 和 React 的职责边界
仓库目前分为三层:
crates/perfcat-core负责进程发现、本机采样、帧聚合和监控会话;src-tauri负责桌面应用状态、命令、事件与本地会话存储;src负责 React 界面、国际化、图表和采样详情。
Rust 适合承接 Windows API、并发采样和明确的数据类型;React 适合处理信息密度较高的交互界面;Tauri 则让两者之间保持一条相对克制的边界。浏览器开发模式还能使用确定性的模拟数据调整 UI,不必每次改样式都启动真实采集。
这个组合并不会自动让桌面应用变简单。真正减少复杂度的,是先把数据模型稳定下来,让前端只负责展示,而不是偷偷承担采样对齐和缺失值推断。
当前边界
0.1.0 只构建和测试 Windows amd64。RTSS 仍然是推荐的 FPS 数据源;ETW Present 尚未覆盖所有图形 API、显示模式和游戏。高权限或受保护进程也可能拒绝访问,GPU 和网络指标的可用性则会受到系统环境与运行权限影响。
这些限制会直接显示在界面和文档中。性能工具首先要让人相信数据,然后才是把图表做得更满。