Performance
Rendering Performance
When rendering an askama template, you should prefer the methods
.render()
(to render the content into a new string),.render_into()
(to render the content into anfmt::Write
object, e.g.String
) or.write_into()
(to render the content into anio::Write
object, e.g.Vec<u8>
)
over .to_string()
or format!()
.
While .to_string()
and format!()
give you the same result, they generally perform much worse
than askama's own methods, because fmt::Write
uses dynamic methods calls instead of
monomorphised code. On average, expect .to_string()
to be 100% to 200% slower than .render()
.
Faster Rendering of Custom Types
Every type that implements fmt::Display
can be used in askama expressions: {{ value }}
.
Rendering with fmt::Display
can be slow, though, because it uses dynamic methods calls in its
fmt::Formatter
argument. To speed up rendering (by a lot, actually),
askama adds the trait FastWritable
. For any custom type you want to render,
it has to implement fmt::Display
, but if it also implements FastWritable
,
then – using autoref-based specialization – the latter implementation is automatically preferred.
To reduce the amount of code duplication, you can let your fmt::Display
implementation call
your FastWritable
implementation:
use std::fmt::{self, Write};
use askama::{FastWritable, NO_VALUES};
// In a real application, please have a look at
// https://github.com/kdeldycke/awesome-falsehood/blob/690a070/readme.md#human-identity
struct Name<'a> {
forename: &'a str,
surname: &'a str,
}
impl fmt::Display for Name<'_> {
// Because the method simply forwards the call, it should be `inline`.
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// `fmt::Write` has no access to runtime values,
// so simply pass `NO_VALUES`.
self.write_into(f, NO_VALUES)?;
Ok(())
}
}
impl FastWritable for Name<'_> {
fn write_into<W: fmt::Write + ?Sized>(
&self,
dest: &mut W,
_values: &dyn askama::Values,
) -> askama::Result<()> {
dest.write_str(self.surname)?;
dest.write_str(", ")?;
dest.write_str(self.forename)?;
Ok(())
}
}
#[test]
fn both_implementations_should_render_the_same_text() {
let person = Name {
forename: "Max",
surname: "Mustermann",
};
let mut buf_fmt = String::new();
write!(buf_fmt, "{person}").unwrap();
let mut buf_fast = String::new();
person.write_into(&mut buf_fast, NO_VALUES).unwrap();
assert_eq!(buf_fmt, buf_fast);
assert_eq!(buf_fmt, "Mustermann, Max");
}
Slow Debug Recompilations
If you experience slow compile times when iterating with lots of templates, you can compile Askama's derive macros with a higher optimization level. This can speed up recompilation times dramatically.
Add the following to Cargo.toml
or .cargo/config.toml
:
[profile.dev.package.askama_derive]
opt-level = 3
This may affect clean compile times in debug mode, but incremental compiles will be faster.