在 eframe 应用中集成 puffin 性能分析器:puffin_profiler 示例深度解析
发布时间:2026/9/10 21:48:41 锦皓数字建站

在 eframe 应用中集成 puffin 性能分析器puffin_profiler 示例深度解析【免费下载链接】eguiegui: an easy-to-use immediate mode GUI in Rust that runs on both web and native项目地址: https://gitcode.com/GitHub_Trending/eg/egui本文以 egui 仓库中的 puffin_profiler 示例 为核心讲解如何将 puffinEmbark Studios 出品的 Rust 即时模式 CPU 性能分析库接入基于eframe的桌面应用并通过puffin_viewer实时查看帧时间线、函数调用火焰图与性能尖峰。读完本文你将掌握 puffin 服务端的启动方式、puffin::profile_scope!宏的埋点手法、puffin_viewer的连接流程以及如何分析示例中刻意制造的 50ms 睡眠尖峰和多视口viewport场景下的性能行为。一、示例概览一个开箱即用的性能分析闭环该示例位于 examples/puffin_profiler/它演示了用 puffin 分析一个eframe应用的最简完整闭环被分析端App一个用eframe::run_native启动的普通 egui 应用src/main.rs采集端Server通过puffin_http::Server::new(127.0.0.1:8585)在本地 8585 端口开启 HTTP 服务接收 puffin 采集的数据可视化端Viewerpuffin_viewer作为独立 GUI 程序连接该端口实时绘制时间线火焰图。三者在同一台机器上构成完整的“采集 → 传输 → 可视化”链路。这是性能优化工作流中非常典型的形态把分析工具与业务代码解耦业务侧只负责埋点分析侧独立查看互不阻塞。二、快速开始两条命令跑通全流程原文档给出了最精简的启动步骤这里结合仓库实际配置展开说明cargo run -p puffin_profiler cargo install puffin_viewer puffin_viewer --url 127.0.0.1:8585其中第一条命令在后台编译并启动示例应用。示例的包名puffin_profiler定义在 examples/puffin_profiler/Cargo.toml 中由于该目录被 workspace 的examples/*通配符收录见根 Cargo.toml因此可以直接用-p puffin_profiler指定包运行。第二条命令先安装官方可视化工具puffin_viewer随后连接到示例应用开启的 8585 端口。连接成功后窗口内会出现一条条帧frame的火焰图时间线每条帧展示了puffin::profile_scope!标记的作用域耗时分布。注意cargo install puffin_viewer需要联网拉取 puffin_viewer 及其依赖并编译首次安装耗时较长而 8585 是示例中硬编码的默认端口如需修改可调整start_puffin_server中的地址。应用窗口本身会显示一行可直接复制的连接命令旁边还有“”复制按钮点击“Click to sleep a bit. That should be visible as a spike in the profiler view!”按钮会让主线程睡眠 50ms在分析器里表现为一柱明显的尖峰用于验证链路是否打通。三、底层原理采集、传输与可视化如何协作3.1 依赖组成示例的依赖声明在 examples/puffin_profiler/Cargo.tomleframe { workspace true, features [ default, __screenshot, # 便于用 EFRAME_SCREENSHOT_TO 导出截图 ] } env_logger { workspace true, features [auto-color, humantime] } log.workspace true puffin.workspace true puffin_http.workspace true profiling { workspace true, features [profile-with-puffin] }各依赖的职责依赖版本workspace 统一管理作用puffin0.20.0采集端核心库提供profile_scope!宏、作用域计时与帧管理puffin_http0.17.0把 puffin 采集的数据通过 HTTP/WebSocket 推送给查看端profiling1.0开启profile-with-puffin通用 profiling 门面把profile_scope!调用编译期映射到 puffin 实现env_loggerlog0.11 / 0.4把 puffin 启动信息、错误信息输出到 stderr版本号统一收敛在根 Cargo.toml 的[workspace.dependencies]中保证整个仓库多个示例/应用使用同一版本。3.2 服务端启动的完整逻辑start_puffin_server()src/main.rs是整条链路的枢纽fn start_puffin_server() { puffin::set_scopes_on(true); // 告诉 puffin 开始收集数据 match puffin_http::Server::new(127.0.0.1:8585) { Ok(puffin_server) { log::info!(Run: cargo install puffin_viewer puffin_viewer --url 127.0.0.1:8585); std::process::Command::new(puffin_viewer) .arg(--url) .arg(127.0.0.1:8585) .spawn() .ok(); // 用 mem::forget 防止 server 被 drop 而关闭 core::mem::forget(puffin_server); } Err(err) { log::error!(Failed to start puffin server: {err}); } } }这段代码有三个值得注意的工程细节puffin::set_scopes_on(true)是采集的总开关。puffin 默认不采集任何作用域数据避免性能开销必须显式打开才会记录profile_scope!命中的数据。服务器句柄故意泄漏core::mem::forgetpuffin_http::Server被 drop 时端口即关闭这里为了让服务器与应用同生命周期刻意用mem::forget防止析构并加了#[expect(clippy::mem_forget)]抑制 clippy 告警。注释也点明了这一点“Dropping it closes the server, so lets not drop it!”。应用会尝试自动拉起puffin_viewerstd::process::Command::new(puffin_viewer)...spawn().ok()会尝试调用本机已安装的puffin_viewer连接自身。如果尚未安装则静默失败.ok()吞掉错误不会影响应用运行——这正是为什么 README 要求先cargo install puffin_viewer。3.3 调用时机在main中尽早启动start_puffin_server()在main()中、eframe::run_native之前被调用src/main.rs并有注释提醒// NOTE: you may only want to call this if the users specifies some flag or clicks a button!即示例为了演示简便而无条件开启服务但生产实践中应通过命令行参数如--puffin或界面按钮按需开启避免所有用户都背负网络监听与采集开销。这一点与 egui 官方演示应用egui_demo_app的做法一致——它把start_puffin_server放在puffinfeature 之后见 crates/egui_demo_app/src/main.rs并通过 feature gate 控制是否编译采集逻辑。3.4 埋点profile_scope! 宏示例在三个位置埋入了分析作用域模拟尖峰src/main.rsif ui .button(Click to sleep a bit. That should be visible as a spike in the profiler view!) .clicked() { puffin::profile_scope!(long_sleep); std::thread::sleep(core::time::Duration::from_millis(50)); }按钮点击后主线程阻塞 50ms在查看器中long_sleep作用域会形成一根异常突出的柱子用来直观验证“哪段代码在拖慢帧率”。立即模式子视口src/main.rs在show_viewport_immediate闭包内埋点puffin::profile_scope!(immediate_viewport)统计该视口每帧渲染耗时。延迟模式子视口src/main.rs在show_viewport_deferred闭包内埋点puffin::profile_scope!(deferred_viewport)覆盖另一类多视口使用方式。由于整个 egui 渲染循环本身也是可被 profile 的egui库在编译时如果开启了profile-with-puffinfeature参考 crates/egui/src/lib.rs 的 feature 文档egui 内部的绘制、布局等关键路径也会自动纳入 puffin 采集与示例自己埋的作用域一起呈现。3.5 日志初始化细节main()开头有一段对RUST_LOG的处理src/main.rslet rust_log std::env::var(RUST_LOG).unwrap_or_else(|_| info.to_owned()); // SAFETY: 在 main 线程、无其他线程运行时调用是安全的 unsafe { std::env::set_var(RUST_LOG, rust_log); }; env_logger::init(); // 输出到 stderr若以 RUST_LOGdebug 运行则输出调试日志它保证env_logger至少以info级别初始化从而让log::info!(Run: cargo install puffin_viewer ...)与端口绑定失败时的log::error!能正常打印到终端方便排查链路问题。四、App 侧代码结构在多视口场景下验证性能MyApp实现了eframe::Appsrc/main.rs主面板包含标题与提示展示“如何使用 egui puffin profiler”的说明连接命令 复制按钮一键复制puffin_viewer连接命令Keep repainting 复选框勾选后每帧调用ui.request_repaint()强制持续重绘并显示 spinner保证即使没有交互事件分析器也能看到连续的帧取消勾选则退化为“仅在事件如鼠标移动时重绘”体现两种帧驱动模式对分析的影响睡眠按钮制造 50ms 尖峰两个子视口开关分别用show_viewport_immediate与show_viewport_deferred创建 200×100 的独立窗口各自带puffin::profile_scope!埋点并在视口内通过ui.input(|i| i.viewport().close_requested())检测关闭请求以同步父级状态。这两个视口用ArcAtomicBool跨线程共享显示状态其中 deferred 视口通过Arc::clone把标志移入闭包src/main.rs是典型的多视口 eframe 应用写法同时能验证 puffin 在多视口下的时间线归属是否清晰。另外示例在ui()回调中直接使用egui::CentralPanel这是该版本eframe::Apptrait 的回调形式并搭配了eframe::NativeOptions中按需启用的 wgpu 渲染器见 Cargo.toml 的wgpufeature 与 main.rs 的#[cfg(feature wgpu)] renderer: eframe::Renderer::Wgpu。运行时可追加--features wgpu切换到 wgpu 后端进行对比分析cargo run -p puffin_profiler --features wgpu五、分析视角能从时间线里读到什么连接puffin_viewer后典型的观察路径是确认连续帧勾选 “Keep repainting” 时时间线会持续出现帧取消后帧变稀疏仅事件驱动可直接对比两种驱动方式的帧率差异。定位尖峰点击睡眠按钮后对应帧中的long_sleep作用域呈一条 50ms 的长条其宽度远大于其他绘制作用域——这就是“哪段逻辑拖慢了帧”的直观可视化。把同样方法套用到真实业务代码即可量化每个函数对帧时间的贡献。多视口开销对比同时打开 immediate 与 deferred 两个子视口查看各自作用域的耗时评估多视口渲染在目标后端上的真实成本。库内耗时若以profile-with-puffin特性构建 egui时间线还会包含 egui 内部布局、文本、绘制等作用域可进一步区分“业务代码慢”还是“框架开销大”。六、接入自有项目的迁移清单将示例移植到自己的eframe应用只需四步添加依赖在Cargo.toml中加入puffin、puffin_http版本可参考仓库 Cargo.toml 的 0.20.0 / 0.17.0如需捕获 egui 内部作用域则为egui/eframe开启profiling/profile-with-puffin或如示例般直接引入profilingcrate。按需启动服务把start_puffin_server逻辑拷入项目建议用命令行参数或 feature 控制避免默认开启参考 crates/egui_demo_app/src/main.rs 的 feature gate 写法。在关键函数埋点用puffin::profile_scope!(name)包裹可疑的热点路径、每帧逻辑或事件回调。查看与分析cargo install puffin_viewer后执行puffin_viewer --url 127.0.0.1:8585结合时间线与尖峰定位瓶颈优化后重新运行对比。七、小结puffin_profiler 示例完整展示了“埋点采集puffin→ HTTP 传输puffin_http→ 独立可视化puffin_viewer”这一成熟的 Rust 性能分析工作流也演示了 eframe 应用在多视口、持续重绘/事件驱动重绘等场景下的性能观察手法。以此为模板你可以为自己的 egui/eframe 应用建立可重复的性能回归检查把耗时过高的作用域视为“尖峰”在每次改动后对比时间线量化优化收益。更完整的性能分析实践还可参考仓库的 ARCHITECTURE.md 与 scripts/find_bloat.sh体积分析等配套工具链。【免费下载链接】eguiegui: an easy-to-use immediate mode GUI in Rust that runs on both web and native项目地址: https://gitcode.com/GitHub_Trending/eg/egui创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。