十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

dbt-jinja 基准测试指南:用 Criterion 追踪 minijinja 引擎性能并横向对比主流模板引擎

dbt-jinja 基准测试指南:用 Criterion 追踪 minijinja 引擎性能并横向对比主流模板引擎 dbt-jinja 基准测试指南用 Criterion 追踪 minijinja 引擎性能并横向对比主流模板引擎【免费下载链接】dbtdbt enables data analysts and engineers to transform their data using the same practices that software engineers use to build applications.项目地址: https://gitcode.com/GitHub_Trending/db/dbt导读本文聚焦于 dbt 开源仓库中 dbt-jinja 项目自带的基准测试套件位于 crates/dbt-jinja/benchmarks它既承担着对 minijinja 模板引擎自身的随时间演进追踪parse / compile / render 三个关键阶段也负责与handlebars、liquid、tera、askama、rinja等主流 Rust 模板引擎的横向对比。读完本文你将掌握如何用一条cargo bench命令复现这套基准、理解每个基准函数在测什么、看懂比较结果数据并能从源码层面理解为什么 minijinja 在编译与渲染阶段表现出不同特性。基准测试套件概览依据 benchmarks/README.md这是一套「刚刚起步beginning」但结构清晰的基准测试套件目前包含两类目标引擎自身基准engine benchmarks追踪 minijinja 在解析、编译、渲染等环节的性能随时间的变化用于防止回归对比基准comparison benchmarks在相同模板语义下与handlebars、liquid、tera以及编译期模板askama、rinja做横向对比。整套基准基于 criterion.rsCriterion 基准测试框架实现它内置统计检验、回归检测和 HTML 报告生成能力非常适合「追踪随时间变化」这一目标。运行方式在crates/dbt-jinja/benchmarks目录下或仓库根目录下使用-p benchmarks指定包执行$ cargo benchCriterion 会依次运行全部[[bench]]目标并在完成后输出每个基准的置信区间估计time: [lower mean upper]。基准测试的工程配置从 Cargo.toml 可以完整还原这套基准的构建方式[package] name benchmarks version 0.1.0 edition 2018 [features] speedups [minijinja/speedups] [dependencies] askama 0.12.1 handlebars 5.1.2 liquid 0.26.1 minijinja { path ../minijinja, default-features false, features [ unstable_machinery, multi_template, builtins, serde, ] } rinja 0.3.4 serde { workspace true } tera 1.17.1 [dev-dependencies] criterion { version 0.5.1, features [html_reports] } [[bench]] name templates harness false [[bench]] name comparison harness false几个值得注意的配置点minijinja以路径依赖引入path ../minijinja即基准直接对本仓库内的 minijinja 源码进行评测而不是 crates.io 上的发布版本这正是「追踪引擎自身随时间变化」的前提通过 feature 组合unstable_machinery、multi_template、builtins、serde显式开启解析器机械结构machinery、多模板、内置函数与 Serde 支持default-features false则关闭默认特性保持测试环境的可控性单独的speedupsfeature 可一键开启minijinja/speedupsminijinja 的可选速度优化特性方便对比开启与否的性能差异两个[[bench]]目标都声明harness false即不使用 Rust 标准测试 harness改由 Criterion 接管基准调度criterion开启了html_reportsfeature运行结束后会在target/criterion/report生成可视化 HTML 报告askama、rinja分别通过 askama.toml 与 rinja.toml 中的dirs [inputs]配置模板搜索目录。引擎自身基准parse / compile / render 三阶段追踪引擎自身基准定义在 benches/templates.rs 中它使用criterion_main!注册单个criterion_benchmark分组覆盖 minijinja 的三个核心阶段1. parse纯语法解析fn do_parse() { parse( black_box(include_str!(../inputs/all_elements.html)), all_elements.html, Default::default(), Default::default(), ) .unwrap(); }这里直接调用minijinja::machinery::parse对模板源码做词法与语法层面的解析尚未编译成可执行指令基准名为parse。black_box用于阻止编译器将常量输入优化掉确保真实测量解析开销。2. compile解析并编译fn do_parse_and_compile() { let mut env Environment::new(); env.add_template( all_elements.html, include_str!(../inputs/all_elements.html), ) .unwrap(); }compile基准将解析与编译合并为一次Environment::add_template调用反映模板「注册进环境」这一实际使用场景的开销。3. render完整渲染fn do_render(env: Environment) { let tmpl env.get_template(all_elements.html).unwrap(); tmpl.render(context! { DEBUG false, site context! { nav vec![ context!{url /, is_active true, title Index}, context!{url /doc, is_active false, title Docs}, context!{url /help, is_active false, title Help}, ] }, items (0..200).skip(3).collect::Vec_(), }) .unwrap(); }渲染基准通过create_real_env()构建了一个更接近生产环境的实例——额外注册了footer.html子模板、自定义过滤器asset_url与自定义函数current_yearfn create_real_env() - Environmentstatic { let mut env Environment::new(); env.add_template(footer.html, include_str!(../inputs/footer.html)) .unwrap(); env.add_template( all_elements.html, include_str!(../inputs/all_elements.html), ) .unwrap(); env.add_filter(asset_url, |_: State, value: String| Ok(value)); env.add_function(current_year, |_: State| Ok(2022)); env }渲染上下文包含一个 197 项的列表(0..200).skip(3)确保基准覆盖真实的循环与过滤链工作量。测试模板all_elements.htmlinputs/all_elements.html 是这个基准的「全家桶」模板几乎覆盖了 minijinja 的核心语法元素这正是命名为 all_elements 的原因!doctype html meta charsetutf-8 title{% block title %}{% endblock %} | Hello/title link relstylesheet href{{ styles/index.css|asset_url }} {# this is a comment #} body {% block html_body %} header h1Hello/h1 nav ul classnav {% for item in site.nav %} lia href{{ item.url }}{% if item.is_active %} classactive{% endif %}{{ item.title|upper }}/a {% endfor %} /ul /nav /header main {% block body %} ul {% for item in items %} li{{ loop.index }}: {{ item|upper }}/li {% endfor %} /ul {% endblock %} /main footer {% block footer %} {% with copyrightcurrent_year() %} {% include footer.html %} {% endwith %} {% endblock %} /footer {% if DEBUG %} pre classdebug{{ debug() }}/pre {% endif %} {% endblock html_body %} /body它一次性覆盖了块继承{% block %}、注释{# ... #}、for循环与loop.index、if条件、过滤器|upper、|asset_url、with作用域、include子模板引入、函数调用current_year()、debug()以及site.nav/item.url这类嵌套属性访问。模板中还包含footer.html的 include定义于 inputs/footer.html使渲染链路更完整。跨引擎对比基准相同语义、不同语法对比基准定义在 benches/comparison.rs 中注册了cmp_compile与cmp_render两个分组。统一的基准上下文为了让对比公平各引擎共享同一份序列化上下文。Context派生Serialize内部包含导航列表site.nav、版权年份与标题并通过宏为Contextaskama 用与RinjaContextrinja 用生成相同的Default实现#[derive(Serialize, Debug, askama::Template)] #[template(path comparison/askama.html)] struct Context { items: VecString, site: Site, title: static str, }items为 6 个字符串条目site.nav为 4 个导航项其中一个标记为激活态。各引擎的输入模板分布在 inputs/comparison 目录下包括minijinja.html、tera.html、liquid.html、handlebars.html、askama.html及其各自的*_footer.html子模板。这些模板在结构上保持同构导航循环、条目循环、页脚 include仅在语法上遵循各引擎方言——例如 liquid 使用{{ item.title|upcase }}与{% for %}/{% include %}语法liquid.htmlhandlebars 使用{{#each items}}与{{ footer.html}}语法handlebars.html而 tera 与 minijinja 同为 Jinja 系语法模板几乎一致tera.html 与 minijinja.html。cmp_compile编译性能对比该分组在b.iter内反复向各引擎注册同一对模板主模板 footer 子模板minijinja每次迭代执行两次env.add_templatetera每次迭代执行两次tera.add_raw_templateliquid每次迭代用ParserBuilder::with_stdlib()解析两次模板handlebars每次迭代执行两次hbs.register_template_string。由于 askama/rinja 属于编译期模板模板在 Rust 编译阶段完成编译它们不参与编译基准——这一点 README 中也有专门说明。cmp_render渲染性能对比该分组在循环外完成模板注册b.iter内仅执行渲染并针对各引擎特点做了等价适配minijinjaenv.get_template(template.html).unwrap().render(Context::default())tera先tera::Context::from_serialize(Context::default())再tera.render(...)liquid使用EagerCompilerInMemorySource预编译 partialsliquid::to_object转换上下文后渲染handlebars通过handlebars_helper!注册upper辅助函数后渲染rinja / askama直接调用Template::render(context)——由于模板是编译期生成的 Rust 代码渲染路径没有字符串解析与运行时编译开销。模板语义的对齐细节对比模板刻意保留了各引擎的惯用写法而非机械翻译minijinja 与 tera 的模板文件在页脚处都通过{% include footer.html %}引入子模板handlebars 用 partial{{ footer.html}}liquid 则通过EagerCompiler预编译 partial。这种「语义等价、语法惯用」的设计使得对比结果反映的是各引擎在真实使用方式下的表现而非被强行统一语法后的失真数据。对比结果与解读README 记录了在MacBook Pro 162021 款上的一次实测结果单位为微秒Criterion 输出置信区间[lower mean upper]cmp_compile/handlebars time: [47.043 µs 47.163 µs 47.295 µs] cmp_compile/liquid time: [28.669 µs 28.776 µs 28.919 µs] cmp_compile/minijinja time: [4.6921 µs 4.7003 µs 4.7092 µs] cmp_compile/tera time: [35.145 µs 35.227 µs 35.315 µs] cmp_render/askama time: [1.5744 µs 1.5793 µs 1.5846 µs] cmp_render/handlebars time: [6.3056 µs 6.3221 µs 6.3399 µs] cmp_render/liquid time: [11.364 µs 11.394 µs 11.426 µs] cmp_render/minijinja time: [5.0475 µs 5.0600 µs 5.0739 µs] cmp_render/tera time: [7.1101 µs 7.1285 µs 7.1482 µs]如何阅读这组数据编译阶段cmp_compileminijinja 约 4.70 µs明显快于 tera约 35.2 µs、liquid约 28.8 µs与 handlebars约 47.2 µs差距在一个数量级左右。这与 minijinja 把解析与编译分离、且解析器轻量的实现思路一致渲染阶段cmp_renderaskama 约 1.58 µs 领先符合其「编译期模板 静态类型」的定位minijinja 约 5.06 µs介于 handlebars约 6.32 µs与 tera约 7.13 µs之间明显快于 liquid约 11.4 µs。两个必须注意的边界Askama 没有编译基准。README 明确指出Askama以及同类的 rinja在 Rust 构建阶段就完成了模板编译并采用静态类型检查因此它没有cmp_compile数据。换言之cmp_render中 askama 的领先是以「编译期成本转移到构建过程」为代价的不能简单解读为「渲染一定更快」数据是特定环境快照。以上数字来自 2021 款 MacBook Pro 16是某一时刻的样本不是可移植的性能结论。要复现或验证请在本仓库中自行运行cargo bench并结合 Criterion 的target/criterion/reportHTML 报告查看置信区间与回归趋势。在 dbt 项目中的定位从仓库结构看minijinja 是被 dbt 深度依赖的 Jinja 模板引擎实现minijinja 为该仓库内维护的模板引擎dbt-jinja家族还包含 dbt-jinja-ctx、dbt-jinja-filters、dbt-jinja-utils、dbt-jinja-vars 等配套 crate。在这条技术栈上模板引擎的解析、编译、渲染性能直接影响到大规模 dbt 项目的模型编译与渲染耗时因此引擎自身基准parse / compile / render承担回归守护职责任何一次针对解析器或渲染器的改动都可以通过cargo bench观察耗时是否出现统计显著的回退跨引擎对比基准则作为参考锚点帮助团队在引入新特性如speedupsfeature时量化收益。总结dbt-jinja 的 benchmark 套件虽然自称「刚起步」但已经覆盖了模板引擎最核心的性能维度基准分组所在文件测量内容parse / compile / renderbenches/templates.rsminijinja 引擎自身三阶段耗时cmp_compilebenches/comparison.rsminijinja vs tera / liquid / handlebars 的模板编译耗时cmp_renderbenches/comparison.rs上述引擎 askama / rinja 的渲染耗时复现与扩展这套基准非常简单直接运行cargo bench即可获得含置信区间的统计结果与 HTML 报告。结合 Cargo.toml 中预留的speedupsfeature你还可以一键对比 minijinja 在开启速度优化前后的差异——这为后续的性能调优工作提供了一个开箱即用的测量起点。【免费下载链接】dbtdbt enables data analysts and engineers to transform their data using the same practices that software engineers use to build applications.项目地址: https://gitcode.com/GitHub_Trending/db/dbt创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表