1 #[macro_use]
2 extern crate serde_derive;
3
4 use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
5 use tera::{Context, Template, Tera, Value};
6
7 static VARIABLE_ONLY: &str = "{{product.name}}";
8
9 static SIMPLE_TEMPLATE: &str = "
10 <html>
11 <head>
12 <title>{{ product.name }}</title>
13 </head>
14 <body>
15 <h1>{{ product.name }} - {{ product.manufacturer | upper }}</h1>
16 <p>{{ product.summary }}</p>
17 <p>£{{ product.price * 1.20 }} (VAT inc.)</p>
18 <p>Look at reviews from your friends {{ username }}</p>
19 <button>Buy!</button>
20 </body>
21 </html>
22 ";
23
24 static SIMPLE_TEMPLATE_LIQUID: &str = "
25 <html>
26 <head>
27 <title>{{ product.name }}</title>
28 </head>
29 <body>
30 <h1>{{ product.name }} - {{ product.manufacturer | upcase }}</h1>
31 <p>{{ product.summary }}</p>
32 <p>£{{ product.price | times: 1.20 }} (VAT inc.)</p>
33 <p>Look at reviews from your friends {{ username }}</p>
34 <button>Buy!</button>
35 </body>
36 </html>
37 ";
38
39 #[derive(Debug, Serialize)]
40 struct Product {
41 name: String,
42 manufacturer: String,
43 price: i32,
44 summary: String,
45 }
46 impl Product {
new() -> Product47 pub fn new() -> Product {
48 Product {
49 name: "Moto G".to_owned(),
50 manufacturer: "Motorala".to_owned(),
51 summary: "A phone".to_owned(),
52 price: 100,
53 }
54 }
55 }
56
57 static PRODUCTS_YAML: &str = "
58 username: bob
59 product:
60 name: Moto G
61 manufacturer: Motorola
62 summary: A phone
63 price: 100
64 ";
65
bench_parsing_basic_template(c: &mut Criterion)66 fn bench_parsing_basic_template(c: &mut Criterion) {
67 let mut group = c.benchmark_group("bench_parsing_basic_template");
68 group.bench_function(BenchmarkId::new("render", "tera"), |b| {
69 b.iter(|| Template::new("bench", None, SIMPLE_TEMPLATE));
70 });
71 group.bench_function(BenchmarkId::new("render", "liquid"), |b| {
72 let parser = liquid::ParserBuilder::with_stdlib().build().unwrap();
73 parser
74 .parse(SIMPLE_TEMPLATE_LIQUID)
75 .expect("benchmark template parsing failed");
76 b.iter(|| parser.parse(SIMPLE_TEMPLATE_LIQUID));
77 });
78 group.finish();
79 }
80
bench_rendering_only_variable(c: &mut Criterion)81 fn bench_rendering_only_variable(c: &mut Criterion) {
82 let mut group = c.benchmark_group("bench_rendering_only_variable");
83 group.bench_function(BenchmarkId::new("render", "tera"), |b| {
84 let mut tera = Tera::default();
85 tera.add_raw_template("test.html", VARIABLE_ONLY).unwrap();
86 let mut context = Context::new();
87 context.insert("product", &Product::new());
88 context.insert("username", &"bob");
89
90 b.iter(|| tera.render("test.html", &context));
91 });
92 group.bench_function(BenchmarkId::new("render", "liquid"), |b| {
93 let parser = liquid::ParserBuilder::with_stdlib().build().unwrap();
94 let template = parser
95 .parse(VARIABLE_ONLY)
96 .expect("Benchmark template parsing failed");
97
98 let data: liquid::Object =
99 serde_yaml::from_str(PRODUCTS_YAML).expect("Benchmark object parsing failed");
100
101 template.render(&data).unwrap();
102 b.iter(|| template.render(&data));
103 });
104 group.finish();
105 }
106
bench_rendering_basic_template(c: &mut Criterion)107 fn bench_rendering_basic_template(c: &mut Criterion) {
108 let mut group = c.benchmark_group("bench_rendering_basic_templates");
109 group.bench_function(BenchmarkId::new("render", "tera"), |b| {
110 let mut tera = Tera::default();
111 tera.add_raw_template("bench.html", SIMPLE_TEMPLATE)
112 .unwrap();
113 let mut context = Context::new();
114 context.insert("product", &Product::new());
115 context.insert("username", &"bob");
116
117 b.iter(|| tera.render("bench.html", &context));
118 });
119 group.bench_function(BenchmarkId::new("render", "liquid"), |b| {
120 let parser = liquid::ParserBuilder::with_stdlib().build().unwrap();
121 let template = parser
122 .parse(SIMPLE_TEMPLATE_LIQUID)
123 .expect("Benchmark template parsing failed");
124
125 let data: liquid::Object =
126 serde_yaml::from_str(PRODUCTS_YAML).expect("Benchmark object parsing failed");
127
128 template.render(&data).unwrap();
129 b.iter(|| template.render(&data));
130 });
131 group.finish();
132 }
133
bench_huge_loop(c: &mut Criterion)134 fn bench_huge_loop(c: &mut Criterion) {
135 #[derive(Serialize)]
136 struct DataWrapper {
137 v: String,
138 }
139
140 #[derive(Serialize)]
141 struct RowWrapper {
142 real: Vec<DataWrapper>,
143 dummy: Vec<DataWrapper>,
144 }
145 let real: Vec<DataWrapper> = (1..1000)
146 .map(|i| DataWrapper {
147 v: format!("n={}", i),
148 })
149 .collect();
150 let dummy: Vec<DataWrapper> = (1..1000)
151 .map(|i| DataWrapper {
152 v: format!("n={}", i),
153 })
154 .collect();
155 let rows = RowWrapper { real, dummy };
156
157 let mut group = c.benchmark_group("bench_huge_loop");
158 group.bench_function(BenchmarkId::new("render", "tera"), |b| {
159 let mut tera = Tera::default();
160 tera.add_raw_templates(vec![(
161 "huge.html",
162 "{% for real in rows.real %}{{real.v}}{% endfor %}",
163 )])
164 .unwrap();
165 let mut context = Context::new();
166 context.insert("rows", &rows);
167
168 b.iter(|| tera.render("huge.html", &context));
169 });
170 group.bench_function(BenchmarkId::new("render", "liquid"), |b| {
171 let parser = liquid::ParserBuilder::with_stdlib().build().unwrap();
172 let template = parser
173 .parse("{% for this in real%}{{this.v}}{% endfor %}")
174 .expect("Benchmark template parsing failed");
175
176 let row_wrapper = liquid::to_object(&rows).unwrap();
177
178 template.render(&row_wrapper).unwrap();
179 b.iter(|| template.render(&row_wrapper));
180 });
181 group.finish();
182 }
183
deep_object() -> Value184 fn deep_object() -> Value {
185 let data = r#"{
186 "foo": {
187 "bar": {
188 "goo": {
189 "moo": {
190 "cows": [
191 {
192 "name": "betsy",
193 "age" : 2,
194 "temperament": "calm"
195 },
196 {
197 "name": "elsie",
198 "age": 3,
199 "temperament": "calm"
200 },
201 {
202 "name": "veal",
203 "age": 1,
204 "temperament": "ornery"
205 }
206 ]
207 }
208 }
209 }
210 }
211 }"#;
212
213 serde_json::from_str(data).unwrap()
214 }
215
deep_object_liquid() -> liquid::Object216 fn deep_object_liquid() -> liquid::Object {
217 liquid::object!({
218 "deep_object": {
219 "foo": {
220 "bar": {
221 "goo": {
222 "moo": {
223 "cows": [
224 {
225 "name": "betsy",
226 "age" : 2,
227 "temperament": "calm"
228 },
229 {
230 "name": "elsie",
231 "age": 3,
232 "temperament": "calm"
233 },
234 {
235 "name": "veal",
236 "age": 1,
237 "temperament": "ornery"
238 }
239 ]
240 }
241 }
242 }
243 }
244 }
245 })
246 }
247
bench_access_deep_object(c: &mut Criterion)248 fn bench_access_deep_object(c: &mut Criterion) {
249 let mut group = c.benchmark_group("bench_access_deep_object");
250 group.bench_function(BenchmarkId::new("render", "tera"), |b| {
251 let mut tera = Tera::default();
252 tera.add_raw_templates(vec![(
253 "deep_object.html",
254 "{% for cow in deep_object.foo.bar.goo.moo.cows %}{{cow.temperament}}{% endfor %}",
255 )])
256 .unwrap();
257 let mut context = Context::new();
258 context.insert("deep_object", &deep_object());
259 assert!(tera
260 .render("deep_object.html", &context)
261 .unwrap()
262 .contains("ornery"));
263
264 b.iter(|| tera.render("deep_object.html", &context));
265 });
266 group.bench_function(BenchmarkId::new("render", "liquid"), |b| {
267 let parser = liquid::ParserBuilder::with_stdlib().build().unwrap();
268 let template = parser
269 .parse(
270 "{% for cow in deep_object.foo.bar.goo.moo.cows %}{{cow.temperament}}{% endfor %}",
271 )
272 .expect("Benchmark template parsing failed");
273
274 let data = deep_object_liquid();
275
276 template.render(&data).unwrap();
277 b.iter(|| template.render(&data));
278 });
279 group.finish();
280 }
281
bench_access_deep_object_with_literal(c: &mut Criterion)282 fn bench_access_deep_object_with_literal(c: &mut Criterion) {
283 let mut group = c.benchmark_group("bench_access_deep_object_with_literal");
284 group.bench_function(BenchmarkId::new("render", "tera"), |b| {
285 let mut tera = Tera::default();
286 tera.add_raw_templates(vec![(
287 "deep_object.html",
288 "
289 {% set goo = deep_object.foo['bar'][\"goo\"] %}
290 {% for cow in goo.moo.cows %}{{cow.temperament}}
291 {% endfor %}",
292 )])
293 .unwrap();
294 let mut context = Context::new();
295 context.insert("deep_object", &deep_object());
296 assert!(tera
297 .render("deep_object.html", &context)
298 .unwrap()
299 .contains("ornery"));
300
301 b.iter(|| tera.render("deep_object.html", &context));
302 });
303 group.bench_function(BenchmarkId::new("render", "liquid"), |b| {
304 let parser = liquid::ParserBuilder::with_stdlib().build().unwrap();
305 let template = parser
306 .parse(
307 "
308 {% assign goo = deep_object.foo['bar'][\"goo\"] %}
309 {% for cow in goo.moo.cows %}{{cow.temperament}}
310 {% endfor %}
311 ",
312 )
313 .expect("Benchmark template parsing failed");
314
315 let data = deep_object_liquid();
316
317 template.render(&data).unwrap();
318 b.iter(|| template.render(&data));
319 });
320 group.finish();
321 }
322
323 criterion_group!(
324 benches,
325 bench_parsing_basic_template,
326 bench_rendering_only_variable,
327 bench_rendering_basic_template,
328 bench_huge_loop,
329 bench_access_deep_object,
330 bench_access_deep_object_with_literal,
331 );
332 criterion_main!(benches);
333