ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

rolldown 内存分配追踪工具 `track_memory_allocations` 实战指南:用分配计数快照守护 SourceMap 代码生成热路径

rolldown 内存分配追踪工具 `track_memory_allocations` 实战指南:用分配计数快照守护 SourceMap 代码生成热路径 rolldown 内存分配追踪工具track_memory_allocations实战指南用分配计数快照守护 SourceMap 代码生成热路径【免费下载链接】rolldownFast Rust bundler for JavaScript/TypeScript with Rollup-compatible API.项目地址: https://gitcode.com/GitHub_Trending/ro/rolldowntrack_memory_allocations是 rolldown 仓库中一个特殊的性能守护任务它统计rolldown_sourcemap在每 chunk 组装 sourcemap 合并这条代码生成热路径上的堆分配次数并把结果提交为快照文件由 CI 强制比对。本文将以该任务为核心结合其源码实现与上游rolldown_sourcemap源码讲解它统计什么、为什么用次数而非字节、三个测量场景如何构造、快照如何解读以及开发者如何在本仓库中复现与维护这套基线。一、这个工具解决什么问题在 JavaScript 打包器的代码生成阶段SourceJoiner::joinchunk 组装 sourcemap 合并与collapse_sourcemaps压缩 / 变换链上的 sourcemap 折叠是每条产物 chunk 都要执行的高频路径。这类代码对额外分配极其敏感一次Vec扩容失败、一个容量预分配失效都会让分配次数随 chunk 规模放大最终拖慢整次构建。该任务的思路是固定一组可复现的场景用计数型分配器统计这些操作产生的堆分配次数将结果写入一个受版本控制的快照文件allocs.snapCI 每次重跑工具只要快照发生变化就判定失败.github/workflows/ci.yml。这样一来任何分配回归或改进都会以可审查的 diff 形式浮现出来而不是悄悄混进构建性能里。从源码注释可以确认其定位main.rsTracks the number of heap allocations made byrolldown_sourcemaps per-chunk machinery … CI re-runs this and fails if the snapshot changes.二、核心设计计数分配器 固定分配器2.1 为什么统计次数而不是字节文档给出了明确的取舍依据README分配次数跨平台稳定且与内存压力强相关字节总量会因分配器 / 平台不同而波动与 oxc 的 tracker 采用同一理由。因此该工具在src/main.rs中实现了一个CountingAllocator只维护两个原子计数器NUM_ALLOC与NUM_REALLOCmain.rs不记录字节。2.2 路由到 MiMalloc消除平台差异为了让数字不依赖系统默认分配器的实现细节工具通过#[global_allocator]安装了自定义分配器内部将alloc/alloc_zeroed/realloc/dealloc全部转发给mimalloc_safe::MiMalloc只在转发成功时递增对应计数器main.rs#[global_allocator] static GLOBAL: CountingAllocator CountingAllocator; static NUM_ALLOC: AtomicUsize AtomicUsize::new(0); static NUM_REALLOC: AtomicUsize AtomicUsize::new(0);2.3Allocs与Reallocs的语义区分快照表中有两列语义必须分清README 的 Notes 部分Allocs统计全新分配alloc/alloc_zeroed成功次数Reallocs统计原地扩容realloc次数。一个容易误判的细节给Vec/String预分配容量会把本应发生的realloc移出计数表现为Reallocs下降——这是改进而非回归。反过来当超大 chunk 场景下Reallocs悄悄增多往往意味着容量预分配失效正是本工具要抓的问题详见main.rs中BIG_CHUNK的注释。三、三个测量场景快照如何产生当前快照allocs.snap的基线内容为Scenario | Allocs | Reallocs ---------------------------------------------------------- sourcemap/join_with_sourcemap | 5 | 0 sourcemap/join_no_sourcemap | 1 | 0 sourcemap/collapse_codegen_chain | 8 | 15三个场景全部由collect_rows构造main.rs并刻意把规模拉大到 2000const BIG_CHUNK: u32 2000使只在足够大的 chunk 上才爆发的超线性分配或预分配失效问题暴露出来。场景一sourcemap/join_with_sourcemap构造一个模拟真实 chunk 的SourceJoiner一个prepend_source横幅模拟生产环境 banner / hashbang 路径、BIG_CHUNK个各带真实 per-module sourcemap 的模块、一个append_source页脚main.rs。模块代码由module_source(index)生成——一个合法的 ES module其函数体大小随index变化从而模拟真实应用 chunk 中共享 import 每模块唯一标识符的不规则 token 分布。对应到source_joiner.rs的实现这个场景走的是enable_sourcemap true分支join会按提前累计的names_len/sources_len/tokens_len/token_chunks_len调用ConcatSourceMapBuilder::with_capacity预分配容量再逐 source 推入内容并合并 map。场景二sourcemap/join_no_sourcemap使用不含任何 sourcemap 的纯文本模块构造 joinermain.rs。这是output.sourcemap未开启时最常见的生产构建路径对应source_joiner.rs中enable_sourcemap false的快速分支跳过ConcatSourceMapBuilder也不扫描每个 source 的换行符。基线中它只有 1 次分配正是这条优化路径的直接体现。场景三sourcemap/collapse_codegen_chain先用program_source(BIG_CHUNK)生成一个导出 2000 个函数的大型模块再通过 oxc 的 Parser Codegen 构造三段真实 sourcemap 链美化 → 压缩 → 再美化main.rs。压缩步骤产生非恒等的重映射使collapse_sourcemaps的重映射循环真正工作。在lib.rs的实现中该函数会为链上除最后一张外的每张 map 预计算反向查找表generate_lookup_table再遍历最后一张 map 的 token 逐级溯源。BIG_CHUNK规模的程序意味着数万个 token重映射循环成为主导因此基线中该场景的Reallocs高达 15——这正是容量预分配在超大规模下悄悄失效的最直观体现。四、测量窗口只统计目标操作本身为了保证每个计数只反映rolldown_sourcemap自身的操作工具的测量窗口经过精心设计。measure函数在每个场景执行前重置两个计数器然后运行操作并读取结果main.rsfn measureR(rows: mut VecRow, label: str, op: impl FnOnce() - R) { NUM_ALLOC.store(0, SeqCst); NUM_REALLOC.store(0, SeqCst); let result op(); let allocs NUM_ALLOC.load(SeqCst); let reallocs NUM_REALLOC.load(SeqCst); black_box(result); rows.push(Row { label: label.to_owned(), allocs, reallocs }); }要点有两处输入前置构建joiner、map 链、代码生成全部在measure之外完成。codegen_map使用 oxc 的 Parser/Codegen 生成真实 map但这些分配不计入窗口README Measured window 一节明确说明。black_box防止优化对结果调用std::hint::black_box避免编译器把未使用的返回值整体优化掉而虚增或虚减计数。main函数直接按顺序执行三个场景并写回快照文件main.rs写入路径由CARGO_MANIFEST_DIR拼接得到即tasks/track_memory_allocations/allocs.snap。五、使用方法与 CI 门禁5.1 本地重新生成快照在仓库根目录执行README Usage 部分just allocs # 或: cargo allocs对应的justfile配方为# Update the allocation-count snapshot (tasks/track_memory_allocations/allocs.snap). # Run after changes to rolldown_sourcemap if the allocs CI gate fails. allocs: cargo allocs该命令会重新生成allocs.snap。README 给出的处理原则是如果变更是预期内的改进例如主动消除了部分分配请提交更新后的快照否则需要调查回归——定位是哪次改动让分配次数上升。5.2 CI 中的强制比对.github/workflows/ci.yml中的门禁逻辑如下cargo allocs git diff --exit-code -- tasks/track_memory_allocations/allocs.snap || (echo Allocations have changed. Run just allocs to update the snapshot, otherwise please fix the regression. exit 1)即CI 先重跑工具再用git diff --exit-code校验快照是否变化一旦有变化即失败并给出明确的下一步指引更新快照或修复回归。5.3 权威平台与本地差异README Canonical platform 一节说明快照在Linux CI runner 上生成并校验以 CI 为准。虽然计数按设计是平台无关的本地just allocs通常应与 CI 一致但若在你自己的 OS / 架构上出现差异应以 CI 结果为准。六、与rolldown_tracking_allocator的分工仓库里还有一个功能相关的 craterolldown_tracking_allocator。它的TrackingAllocator同样包装 MiMalloc但统计的是live_bytes/peak_bytes/alloc_count/realloc_count并针对多线程整构建场景做了按线程批量冲刷的优化每个线程先在 thread-local 累积增量达到FLUSH_BYTES 64 * 1024或FLUSH_OPS 1024才并入全局原子量以规避单组全局原子量在高并发下的缓存行竞争。二者在设计上明确分工lib.rs的模块注释tasks/track_memory_allocationsuses the simpler unbatched counting for single-threaded CI snapshots, where exactness matters and contention does not exist.对比结论维度track_memory_allocationsrolldown_tracking_allocator计量对象仅分配次数Allocs / Reallocs存活字节、峰值字节、分配次数计数方式简单原子计数器未批量按线程批量冲刷thread-local 累积使用场景单线程、确定性优先的 CI 快照多线程整构建的运行期追踪分配器mimalloc_safe::MiMallocMiMalloc七、维护指南依赖升级会合法地移动快照README Dependency bumps 一节给出一个重要的维护预期快照数值依赖rolldown_sourcemap及其传递依赖oxc/oxc_sourcemap的内部实现。因此升级依赖时若其分配行为发生变化快照发生移动是合法且预期的正确做法是运行just allocs重新生成快照并审阅 diff 属于预期 churn后提交真正需要警惕的是在没有任何相关改动的情况下快照漂移——那才是隐藏回归的信号。从任务定义Cargo.toml可以看到其依赖被刻意收紧为三样mimalloc-safe、oxc用于生成真实 sourcemap、rolldown_sourcemap被测对象且该二进制test false、doctest false不参与测试框架纯粹作为一个可重复执行的测量入口存在。八、总结track_memory_allocations用一份仅 3 行数据的快照文件为 rolldown 的 sourcemap 代码生成热路径建立了一条低成本、高灵敏度的性能基线以分配次数取代分配字节换取跨平台稳定性以固定分配器消除环境差异以 2000 模块规模的场景放大隐藏回归再以 CIgit diff --exit-code强制每个改动接受审查。任何接触rolldown_sourcemap的开发者都应把just allocs产生的 diff 当作评估改动性能影响的第一手证据。【免费下载链接】rolldownFast Rust bundler for JavaScript/TypeScript with Rollup-compatible API.项目地址: https://gitcode.com/GitHub_Trending/ro/rolldown创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表