Rendering mustache improvements

Hello.

I have been looking at rendering mustache templates and the results were performant when using small text samples. When increasing the test data size with a much bigger template and parameters, the performance is notably decreased. I have looked at other implementations and that is consistent of around one minute to render one million times with either test sample. For the Rust one it went from 2 seconds for the small to 90 seconds for the larger one, which is quite varied. It would probably be expected to increase in time with a larger data sample, but I wasn't thinking it would be that much different.

I was wondering if there were any suggestions for this sample, such as either buffering or using another approach to get it more consistent.

I am iterating over the items first and converting to string as some may be nil/null values which breaks the rendering. This would be an empty string. Maybe there is another way to only convert nils, as most items are strings already.

lib.rs

pub fn wrapper(template: String, params: RHash) -> Result<String, Error> {
    let mut data: HashMap<String, String> = HashMap::new();

    params.foreach(|key: Symbol, value: Value| {
        data.insert(key.to_string(), value.to_string());

        return Ok(ForEach::Continue);
    })?;

    return renderer::render(template, data)
        .map_err(|e| Error::new(runtime_error(), e.to_string()));
}

renderer.rs

extern crate mustache;

pub fn render(template: String, params: HashMap<String, String>) -> Result<String, Error> {
    let template = mustache::compile_str(&template).expect("Failed to compile");
    let mut bytes = vec![];

    template
        .render(&mut bytes, &params)
        .expect("Failed to render");

    return Ok(String::from_utf8(bytes).expect("Failed to encode string"));
}

Any suggestions on this is much appreciated.

Are you compiling and running the code with optimizations enabled? (cargo run --release)

It is using --profile release when it builds.

Finished `release` profile [optimized] target(s) in 0.02s

Have you been able to verify how much of the time increase is from the data HashMap rebuilding, versus the actual template execution?
That may shed some light on which part to focus on improving (if one overshadows the other).

2 Likes

I have benchmarked the rendering from 1,000 characters up to 800,000 and the same for the hash map insertion. I used Criterion but not benchmarked before. The timings seem to be good for both functions. The rendering is slower as expected.


The only thing I am not benchmarking is the converting of "RHash" and "RSymbol" into Strings as I can't seem to get that to compile outside of the Ruby gem. I don't think it is possible to run the cargo commands against it directly. I was looking at this, as this is what I was running into: Building on aarch64-apple-darwin fails with lots of undefined symbols · Issue #77 · matsadler/magnus · GitHub

I could look to investigate that part by having empty functions in the code to only test the conversion and do this from the Ruby side. The other two parts did not seem to suggest any great slowness.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.