Performance

Rendering Performance

When rendering an askama template, you should prefer the methods

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.