Ruff 性能微基准测试实战ruff_benchmark crate 的运行、回归对比与源码解析【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruffruff_benchmark是 Ruff 仓库中专门用于在单个文件维度上对 linter 与 formatter 进行微基准micro-benchmark测试的 Criterion/Divan crate也是 CONTRIBUTING.md 所描述的基准测试体系里逐文件、逐代码路径层面的关键一环。本文以 crates/ruff_benchmark/README.md 为主线结合 CONTRIBUTING.md 的基准章节与各 bench 源码完整讲解如何运行基线保存、回归对比、按模块过滤等命令并剖析其内部测试文件、基准分组、特性开关与防抖设计帮助你在改造 Ruff 代码后科学地验证有没有变慢。概览ruff_benchmark 在 Ruff 性能体系中的定位Ruff 的性能验证分为三个层面详见 CONTRIBUTING.md 的 Benchmarking and Profiling 一节CPython 全仓库基准用hyperfine比较 Ruff 与同类工具在 CPython 代码库上的端到端耗时微基准microbenchmarks由ruff_benchmarkcrate 在个体文件上分别运行 lexer、parser、linter、formatter并在 PR 上自动执行用于及早发现具体代码路径的回归性能剖析profiling借助微基准或真实项目目录做火焰图等分析。ruff_benchmark承担第 2 项。它的自述非常简洁见 crates/ruff_benchmark/README.mdTheruff_benchmarkcrate benchmarks the linter and the formatter on individual files.本 crate 定位为开发/CI 专用publish false描述为 Ruff Micro-benchmarks见 Cargo.toml其正式使用说明baseline 对比、critcmp PR 对比、常用 tips位于 CONTRIBUTING.md 的 Microbenchmarks 小节README 中的命令正是它的快速入口。快速上手三条核心命令进入仓库根目录后可用如下命令立即测量当前工作区代码的解析与检查性能。其中--之后的所有参数都会原样透传给 Criterion 基准框架# 1. 先在 baseline 上运行一次把结果保存为名为 main 的基线 cargo bench -p ruff_benchmark -- --save-baselinemain # 2. 之后例如改动代码后再次运行与刚才保存的基线对比 cargo bench -p ruff_benchmark -- --baselinemain # 3. 只运行 lexer 相关的基准可按基准名过滤 cargo bench -p ruff_benchmark lexer -- --baselinemain第一条命令会运行 crate 中默认特性下可用的全部基准并存储基线第二条命令则在每次迭代后由 Criterion 自动给出与main基线的统计对比结论improved/regressed。第三条展示了按名称过滤的能力lexer会命中名为lexer的 bench 目标具体过滤规则见下文按名称过滤一节。更快的入口cargo benchmark别名如果你只想快速盯住两条最核心的代码路径linter 与 formatter仓库在 .cargo/config.toml 中预置了别名cargo benchmark它等价于CONTRIBUTING.md 中明确给出cargo bench -p ruff_benchmark --bench linter --bench formatter --也就是说cargo benchmark与全量cargo bench -p ruff_benchmark的区别在于后者还会编译并运行 parser、lexer、ty 类型检查等其余 bench 目标各目标的required-features与特性开关详见下文。运行前注意基准对噪音高度敏感。仓库建议在 CPU 基本空闲时测量关闭浏览器等后台程序若 CPU 支持性能模式performance mode也应开启尤其是针对耗时很短的基准。这与 CONTRIBUTING.md 中的 Note 一致。被测对象代表性测试文件与 TestFile 抽象微基准的载体是若干具有代表性的 Python 源文件。它们在 src/lib.rs 中被定义为静态TestFile通过include_str!在编译期嵌入即测试数据随 crate 一起编译运行时无磁盘 IO常量文件名resources 目录选取意图NUMPY_GLOBALSnumpy/globals.pyNumPy 源码片段典型科学计算代码风格UNICODE_PYPINYINpypinyin.py大量 Unicode/中文字符串处理考验字符串与注释路径PYDANTIC_TYPESpydantic/types.py类型注解密集的现代 Python对 ty 类型检查尤其相关NUMPY_CTYPESLIBnumpy/ctypeslib.py含ctypes用法的 NumPy 模块LARGE_DATASETlarge/dataset.py大体积测试数据源自 mikeio 项目测试文件见 src/lib.rs 中的来源注释这些资源文件真实存在于 crates/ruff_benchmark/resources/ 下另有tomllib/、typeis_narrowing.py等供 ty 相关基准使用的文件。TestFile封装了name与code并提供code()、name()、path()三个方法其中path()会把名称解析回resources目录下的真实路径供 linter 基准按扩展名推导源码类型src/lib.rs。linter、parser、lexer、formatter 四个基准共享完全相同的 5 个测试文件各自create_test_cases()返回同样列表这使不同阶段的耗时具有可比性。基准目标与特性开关feature matrixcrates/ruff_benchmark/Cargo.toml 声明了 5 个特性default聚合了其中 4 个feature含义引入的 bench 目标ruff_instrumented打桩测时 Ruff 各阶段lexer/parser/linter/formatter基于 Criterionlinter、lexer、parser、formatterty_instrumented打桩测时 ty类型检查器内部基于 Criterionty、ty_constraint_setmodule_resolution模块解析基准基于 Divanmodule_resolutionty_walltimety 在真实项目上的墙钟耗时基于 Divanty_script、ty_walltimecodspeed切换 Criterion 兼容层以支持 CodSpeed 平台无每个[[bench]]目标都设置了harness false与对应的required-featuresCargo.toml。因此使用默认特性执行cargo bench -p ruff_benchmark时9 个 bench 目标linter、lexer、parser、formatter、ty、ty_constraint_set、module_resolution、ty_script、ty_walltime全部参与若需要裁剪例如只测 Ruff 不测 ty可cargo bench -p ruff_benchmark --no-default-features --features ruff_instrumented。两套测时框架的取舍也体现在源码中Ruff 工具链linter 等与 ty 打桩基准使用 Criterion因为要配合--save-baseline/--baseline做统计回归而module_resolution、ty_script、ty_walltime三个 bench 使用 Divan。Criterion / CodSpeed 兼容层crates/ruff_benchmark/src/criterion.rs 是一个极简的 re-export 层未启用codspeed特性时直接转发 Criterion 全量 API并把BenchmarkGroup固定为criterion::BenchmarkGroupa, WallTime启用codspeed后则转发codspeed_criterion_compat。代码注释说明这样做的原因CodSpeed 并非所有平台都支持因此需要兼容层按需切换后端。各 bench 源文件统一use ruff_benchmark::criterion;而非直接依赖 criterion正是为这一切换预留的抽象。逐个拆解五个 Ruff 微基准的实现细节linter 基准benches/linter.rsbenches/linter.rs 定义了三个 Criterion 分组覆盖三类规则集linter/default-rules使用LinterSettings::default()即开箱即用的默认规则集linter/all-rules收集RuleSelector::All的全部规则preview 关闭linter/all-with-preview-rules启用PreviewMode::Enabled覆盖含 preview 规则的全集。代码层面有几点值得借鉴的工程细节解析移出计时区先用parse_module解析一次随后用b.iter_batched(|| parsed.clone(), ..., criterion::BatchSize::SmallInput)计时——每次迭代仅克隆已解析的 AST 并执行lint_only传入ParseSource::Precomputed(parsed)linter.rs从而把测 linter和测 parser解耦避免重复解析污染 lint 耗时。剔除 IO 型规则disable_io_rules显式关闭ShebangMissingExecutableFile与ShebangNotExecutable两条与文件系统相关的规则linter.rs原因注释写明Disables IO based rules because they are a source of flakiness即防止基准因 IO 而抖动。吞吐量标注对每个 case 调用Throughput::Bytes(...)让 Criterion 额外报告 MiB/s 等吞吐指标。formatter 基准benches/formatter.rsbenches/formatter.rs 在formatter分组中测量格式化全流程。关键调用链parse(code, ParseOptions::from(Mode::Module))得到带 token 的语法树由 tokens 构建TriviaRanges注释/空白等 trivia 区间通过文件扩展名构造PyFormatOptions并显式with_preview(PreviewMode::Enabled)format_module_ast(...)生成格式化中间表示最后print()输出。这里同样把解析步骤放在b.iter()之外的 setup 阶段formatter.rs计时区只包含真正的格式化与打印。lexer 基准benches/lexer.rsbenches/lexer.rs 的计时逻辑最为纯粹用lexer::lex(code, Mode::Module)建立词法迭代器然后反复next_token()直到遇到EndOfFile若出现Unknowntoken 则直接 panic保证输入是合法 Python。整段迭代都在被测时间之内测的是词法切分的原始吞吐。parser 基准benches/parser.rsbenches/parser.rs 对每个文件反复调用parse_module(...)并断言语法有效。一个细节是使用b.iter_with_large_drop(|| parse_module(...))parser.rs——该 API 会把大对象的析构成本也计入迭代适合测量会产生巨大 AST 分配的 parse 阶段。关于 allocator 的一致性设置为避免内存分配器不同导致基准失真linter/lexer/parser/formatter 四个 bench 文件开头都做了相同的全局分配器替换Windows 上使用mimalloc其余平台非 OpenBSD 且架构为 x86_64/aarch64/powerpc64/riscv64使用tikv-jemallocator见 Cargo.toml 与各 bench 文件。更重要的是jemalloc 会通过导出_rjem_malloc_conf将dirty_decay_ms与muzzy_decay_ms都设为-1linter.rs代码注释解释了原因默认 10s 的 decay 可能表现为随机性的慢分配而基准场景并不需要把未分配页面归还给 OS禁用 decay 后测量更稳定。ty 相关基准类型检查器的微基准与真实项目耗时仓库中 ty类型检查引擎处于开发阶段其性能验证同样收敛到本 crate。这类基准分成两组打桩微基准ty与ty_constraint_set使用 Criterion。ty基准benches/ty.rs包含两路测试一是以标准库tomllib的 4 个文件为微场景的FileCase数据来自 resources/tomllib/二是从 ty 生态固定一批真实仓库做端到端检查。该文件还体现了基准也要防回归的思想通过KeyDiagnosticFields诊断 ID、代码、范围、消息、严重级别构成的元组配合EXPECTED_TOMLLIB_DIAGNOSTICS断言诊断输出符合预期ty.rs从而避免在基准内部使用 insta 快照机制。benches/ty_shared/目录如setup_micro_case、setup_rayon被这些基准共享。墙钟耗时ty_walltime与ty_script改用 Divan直接面向真实项目。其基础设施位于 src/real_world_projects.rs工作方式如下把目标仓库克隆/更新到./target下的缓存目录顶层常量TY_ECOSYSTEM_PIN 2026-06-17T06:46:32Z用于固定依赖时间窗防止外部依赖漂移引入抖动对带依赖的项目用uv创建虚拟环境并安装依赖可选将整个项目结构复制进内存文件系统减少基准中的 IO 噪音随后构造ProjectDatabase并执行基准。例如 benches/ty_walltime.rs 中每次迭代会ProjectMetadata::discover发现项目、覆写Options指定 Python 版本、.venv路径等再以OsSystem打开真实文件系统进行全量检查计时。模块解析基准module_resolutionbenches/module_resolution.rs 测量的是随着额外搜索路径extra_paths数量增长冷模块解析查询批次的耗时变化。它在内存文件系统TestSystem中构造从 0 到 N 个/extra/p{n}目录并用三类查询考察解析器行为预先 seed 的目标模块命中缓存/逐级查找、不存在的模块名练习 stub-overlay 发现后的回退、标准库模块名os、sys、typing等。同一批次内的查询共享底层 resolver 缓存用于评估搜索路径越多冷解析越慢这一核心问题。回归驱动的开发流程baseline 保存与对比这是 crates/ruff_benchmark/README.md 直接指向 CONTRIBUTING.md 的核心用法。Ruff 的微基准建立在 Criterion 之上推荐的基准驱动开发工作流为# 1. 在改动前的代码例如 main 分支保存基线 cargo bench -p ruff_benchmark -- --save-baselinemain # 2. 修改代码后反复迭代对比 cargo bench -p ruff_benchmark -- --baselinemain每次对比后 Criterion 都会打印该基准相对基线的统计变化improved/regressed 及置信区间据此即可判断一次改动是否引入了可观测的退化。按名称过滤基准微基准数量多、耗时长建议只跑关心的那部分。例如# 只运行 lexer 相关基准对应 bench 目标名 lexer cargo bench -p ruff_benchmark lexer -- --baselinemain在 Criterion 中cargo bench -p ruff_benchmark filter的filter会按分组/场景路径的子串匹配因此既可以写 bench 目标名如lexer、parser、formatter、ty也可以写更细的分组名如linter/default-rules或linter/all-rules分组名定义见 benches/linter.rs。例如只测默认规则集下的 lint 性能cargo bench -p ruff_benchmark linter/default-rules常用调试开关CONTRIBUTING.md 的 Tips 补充了两个降低等待成本的开关# 输出更精简省略统计显著性信息 cargo bench -p ruff_benchmark -- --quiet # 更快出结果但更容易受噪音影响 cargo bench -p ruff_benchmark -- --quickPR 场景用 critcmp 生成美观的对比摘要当需要向 PR 评审展示改动带来的提升/回退时仓库推荐--save-baseline配合critcmp工具# 在 main 上保存一份基线 cargo bench -p ruff_benchmark -- --save-baselinemain # 应用改动后保存另一份 cargo bench -p ruff_benchmark -- --save-baselinepr # 对比两份基线 critcmp main prcritcmp需要单独安装README 指向其项目主页可用如下命令安装cargo install --locked critcmp微基准与剖析的衔接微基准的另一个用途是作为 profiling 的固定输入把热点分析收敛到可复现的样本上。仓库给出的两条路径都与ruff_benchmark直接相关Linux perf先以profilingprofile 编译cargo bench -p ruff_benchmark --no-run --profileprofiling再用perf record录制指定时长的基准执行示例命令含--profile-time1即每种场景只跑 1 秒详见 CONTRIBUTING.md 的 Profiling Projects 一节macOS cargo-instrumentscargo instruments -t time --bench linter --profile profiling -p ruff_benchmark -- --profile-time1-t time测墙钟时间-t alloc则用于分析分配还可附加单个测试文件名过滤器只剖析一个场景。注意此类命令依赖仓库中定义的profilingprofile更接近 release 且保留符号运行前请确认当前 toolchain 支持。小结何时使用何种命令目标推荐命令快速自检 linter formatter 有无回归cargo benchmark全量微基准并保存/对比基线cargo bench -p ruff_benchmark -- --save-baselinename/-- --baselinename只测某一个阶段cargo bench -p ruff_benchmark lexer、parser、formatter、ty…精简/快速输出cargo bench -p ruff_benchmark -- --quiet、-- --quickPR 对比摘要分别--save-baselinemain、--save-baselinepr后critcmp main pr进一步阅读与源码入口基准快速说明crates/ruff_benchmark/README.md完整使用指南含 alias、baseline 工作流、critcmp、profilingCONTRIBUTING.md别名配置.cargo/config.toml特性与 bench 目标声明crates/ruff_benchmark/Cargo.toml测试文件与TestFile定义crates/ruff_benchmark/src/lib.rs各基准实现linter、lexer、parser、formatter、ty、ty_walltime、module_resolution等源码位于 crates/ruff_benchmark/benches/真实项目基准基础设施与 Criterion/CodSpeed 兼容层crates/ruff_benchmark/src/real_world_projects.rs、crates/ruff_benchmark/src/criterion.rs需要强调这些基准衡量的是改动前后的相对回归而非对外部工具的绝对结论本文所有数据与结论均以当前仓库文档与源码为准。【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考