#[macro_use] extern crate serde_derive; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use tera::{Context, Template, Tera, Value}; static VARIABLE_ONLY: &str = "{{product.name}}"; static SIMPLE_TEMPLATE: &str = " {{ product.name }}

{{ product.name }} - {{ product.manufacturer | upper }}

{{ product.summary }}

£{{ product.price * 1.20 }} (VAT inc.)

Look at reviews from your friends {{ username }}

"; static SIMPLE_TEMPLATE_LIQUID: &str = " {{ product.name }}

{{ product.name }} - {{ product.manufacturer | upcase }}

{{ product.summary }}

£{{ product.price | times: 1.20 }} (VAT inc.)

Look at reviews from your friends {{ username }}

"; #[derive(Debug, Serialize)] struct Product { name: String, manufacturer: String, price: i32, summary: String, } impl Product { pub fn new() -> Product { Product { name: "Moto G".to_owned(), manufacturer: "Motorala".to_owned(), summary: "A phone".to_owned(), price: 100, } } } static PRODUCTS_YAML: &str = " username: bob product: name: Moto G manufacturer: Motorola summary: A phone price: 100 "; fn bench_parsing_basic_template(c: &mut Criterion) { let mut group = c.benchmark_group("bench_parsing_basic_template"); group.bench_function(BenchmarkId::new("render", "tera"), |b| { b.iter(|| Template::new("bench", None, SIMPLE_TEMPLATE)); }); group.bench_function(BenchmarkId::new("render", "liquid"), |b| { let parser = liquid::ParserBuilder::with_stdlib().build().unwrap(); parser .parse(SIMPLE_TEMPLATE_LIQUID) .expect("benchmark template parsing failed"); b.iter(|| parser.parse(SIMPLE_TEMPLATE_LIQUID)); }); group.finish(); } fn bench_rendering_only_variable(c: &mut Criterion) { let mut group = c.benchmark_group("bench_rendering_only_variable"); group.bench_function(BenchmarkId::new("render", "tera"), |b| { let mut tera = Tera::default(); tera.add_raw_template("test.html", VARIABLE_ONLY).unwrap(); let mut context = Context::new(); context.insert("product", &Product::new()); context.insert("username", &"bob"); b.iter(|| tera.render("test.html", &context)); }); group.bench_function(BenchmarkId::new("render", "liquid"), |b| { let parser = liquid::ParserBuilder::with_stdlib().build().unwrap(); let template = parser .parse(VARIABLE_ONLY) .expect("Benchmark template parsing failed"); let data: liquid::Object = serde_yaml::from_str(PRODUCTS_YAML).expect("Benchmark object parsing failed"); template.render(&data).unwrap(); b.iter(|| template.render(&data)); }); group.finish(); } fn bench_rendering_basic_template(c: &mut Criterion) { let mut group = c.benchmark_group("bench_rendering_basic_templates"); group.bench_function(BenchmarkId::new("render", "tera"), |b| { let mut tera = Tera::default(); tera.add_raw_template("bench.html", SIMPLE_TEMPLATE) .unwrap(); let mut context = Context::new(); context.insert("product", &Product::new()); context.insert("username", &"bob"); b.iter(|| tera.render("bench.html", &context)); }); group.bench_function(BenchmarkId::new("render", "liquid"), |b| { let parser = liquid::ParserBuilder::with_stdlib().build().unwrap(); let template = parser .parse(SIMPLE_TEMPLATE_LIQUID) .expect("Benchmark template parsing failed"); let data: liquid::Object = serde_yaml::from_str(PRODUCTS_YAML).expect("Benchmark object parsing failed"); template.render(&data).unwrap(); b.iter(|| template.render(&data)); }); group.finish(); } fn bench_huge_loop(c: &mut Criterion) { #[derive(Serialize)] struct DataWrapper { v: String, } #[derive(Serialize)] struct RowWrapper { real: Vec, dummy: Vec, } let real: Vec = (1..1000) .map(|i| DataWrapper { v: format!("n={}", i), }) .collect(); let dummy: Vec = (1..1000) .map(|i| DataWrapper { v: format!("n={}", i), }) .collect(); let rows = RowWrapper { real, dummy }; let mut group = c.benchmark_group("bench_huge_loop"); group.bench_function(BenchmarkId::new("render", "tera"), |b| { let mut tera = Tera::default(); tera.add_raw_templates(vec![( "huge.html", "{% for real in rows.real %}{{real.v}}{% endfor %}", )]) .unwrap(); let mut context = Context::new(); context.insert("rows", &rows); b.iter(|| tera.render("huge.html", &context)); }); group.bench_function(BenchmarkId::new("render", "liquid"), |b| { let parser = liquid::ParserBuilder::with_stdlib().build().unwrap(); let template = parser .parse("{% for this in real%}{{this.v}}{% endfor %}") .expect("Benchmark template parsing failed"); let row_wrapper = liquid::to_object(&rows).unwrap(); template.render(&row_wrapper).unwrap(); b.iter(|| template.render(&row_wrapper)); }); group.finish(); } fn deep_object() -> Value { let data = r#"{ "foo": { "bar": { "goo": { "moo": { "cows": [ { "name": "betsy", "age" : 2, "temperament": "calm" }, { "name": "elsie", "age": 3, "temperament": "calm" }, { "name": "veal", "age": 1, "temperament": "ornery" } ] } } } } }"#; serde_json::from_str(data).unwrap() } fn deep_object_liquid() -> liquid::Object { liquid::object!({ "deep_object": { "foo": { "bar": { "goo": { "moo": { "cows": [ { "name": "betsy", "age" : 2, "temperament": "calm" }, { "name": "elsie", "age": 3, "temperament": "calm" }, { "name": "veal", "age": 1, "temperament": "ornery" } ] } } } } } }) } fn bench_access_deep_object(c: &mut Criterion) { let mut group = c.benchmark_group("bench_access_deep_object"); group.bench_function(BenchmarkId::new("render", "tera"), |b| { let mut tera = Tera::default(); tera.add_raw_templates(vec![( "deep_object.html", "{% for cow in deep_object.foo.bar.goo.moo.cows %}{{cow.temperament}}{% endfor %}", )]) .unwrap(); let mut context = Context::new(); context.insert("deep_object", &deep_object()); assert!(tera .render("deep_object.html", &context) .unwrap() .contains("ornery")); b.iter(|| tera.render("deep_object.html", &context)); }); group.bench_function(BenchmarkId::new("render", "liquid"), |b| { let parser = liquid::ParserBuilder::with_stdlib().build().unwrap(); let template = parser .parse( "{% for cow in deep_object.foo.bar.goo.moo.cows %}{{cow.temperament}}{% endfor %}", ) .expect("Benchmark template parsing failed"); let data = deep_object_liquid(); template.render(&data).unwrap(); b.iter(|| template.render(&data)); }); group.finish(); } fn bench_access_deep_object_with_literal(c: &mut Criterion) { let mut group = c.benchmark_group("bench_access_deep_object_with_literal"); group.bench_function(BenchmarkId::new("render", "tera"), |b| { let mut tera = Tera::default(); tera.add_raw_templates(vec![( "deep_object.html", " {% set goo = deep_object.foo['bar'][\"goo\"] %} {% for cow in goo.moo.cows %}{{cow.temperament}} {% endfor %}", )]) .unwrap(); let mut context = Context::new(); context.insert("deep_object", &deep_object()); assert!(tera .render("deep_object.html", &context) .unwrap() .contains("ornery")); b.iter(|| tera.render("deep_object.html", &context)); }); group.bench_function(BenchmarkId::new("render", "liquid"), |b| { let parser = liquid::ParserBuilder::with_stdlib().build().unwrap(); let template = parser .parse( " {% assign goo = deep_object.foo['bar'][\"goo\"] %} {% for cow in goo.moo.cows %}{{cow.temperament}} {% endfor %} ", ) .expect("Benchmark template parsing failed"); let data = deep_object_liquid(); template.render(&data).unwrap(); b.iter(|| template.render(&data)); }); group.finish(); } criterion_group!( benches, bench_parsing_basic_template, bench_rendering_only_variable, bench_rendering_basic_template, bench_huge_loop, bench_access_deep_object, bench_access_deep_object_with_literal, ); criterion_main!(benches);