Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(v2): precompile ETA templates #3238

Merged
merged 2 commits into from
Aug 17, 2020
Merged

refactor(v2): precompile ETA templates #3238

merged 2 commits into from
Aug 17, 2020

Conversation

slorber
Copy link
Collaborator

@slorber slorber commented Aug 7, 2020

Motivation

ETA has a caching system but reading the doc, it's not totally clear if we leverage it correctly (+ current code did .trim() on each SSR page: useless work)

I just precompiled the templates lazily in a memoize call to see if it can improve build times.

cc @nebrelbug if you can review and tell me what you think. I'm not sure we leveraged Eta caching anyway (despite providing a template name), as the default cache option is not documented here: https://eta.js.org/docs/api/configuration

Do you think this PR has an impact?

@slorber slorber requested a review from yangshun as a code owner August 7, 2020 11:45
@slorber slorber added the pr: performance This PR does not add a new behavior, but existing behaviors will be more memory- / time-efficient. label Aug 7, 2020
@facebook-github-bot facebook-github-bot added the CLA Signed Signed Facebook CLA label Aug 7, 2020
@slorber slorber changed the title feat(v2): precompile ETA templates pref(v2): precompile ETA templates Aug 7, 2020
@slorber slorber changed the title pref(v2): precompile ETA templates perf(v2): precompile ETA templates Aug 7, 2020
@slorber slorber changed the title perf(v2): precompile ETA templates refactor(v2): precompile ETA templates Aug 7, 2020

function renderSSRTemplate(data) {
const compiled = getCompiledSSRTemplate();
return compiled(data, {...eta.defaultConfig, ...customEtaConfig});
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nebrelbug I found this part a bit confusing. Why do I need to provide some config in the compiled template. Can't it uses the partial config I set during compilation?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also this looks weird to me to expect a full config here, why not partial + you merge to defaults in the lib directly?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @slorber, good question. You need to provide a config to the compiled template function because the config object holds partials, filters, and other utility methods Eta uses while actually rendering the template.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do accept partial configs in compile and render. Those functions merge passed-in config options with Eta's default configuration.

It's better for performance, though, to not have to merge the config every time we call the compiled function. Plus, in most cases it would be redundant (most users call render() to render a template, which already merges the config before calling the compiled template function and returning the result) 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did realize that in this case, we can just call the compiled function with eta.defaultConfig. The {rmWhitespace: true} option is applied only when the template is compiled, and doesn't affect the template's rendering.

@docusaurus-bot
Copy link
Contributor

docusaurus-bot commented Aug 7, 2020

Deploy preview for docusaurus-2 ready!

Built with commit bab2dd6

https://deploy-preview-3238--docusaurus-2.netlify.app

@slorber
Copy link
Collaborator Author

slorber commented Aug 7, 2020

It does not seem to improve anything 😓

image

@nebrelbug
Copy link
Contributor

Hi @slorber, it looks like you're right: I forgot to set the cache option to true, and Eta doesn't have caching enabled by default!

@nebrelbug
Copy link
Contributor

When Eta compiles a template, it returns a function which can be called with data and a valid configuration object. Since the templates won't change, I might suggest doing something like this:

// createRedirectPageContent.ts

// We don't have to generate this every time because the template won't change
let compiledTemplate = eta.compile(redirectPageTemplate.trim())

function renderRedirectPageTemplate(data: object) {
  return compiledTemplate(data, eta.defaultConfig);
}

// We could memoize the renderRedirectPageTemplate function,
// since it will always return the same result when called with the same data

// Since compiled template functions are very fast, though,
// I'm not sure if it would bring a noticeable performance gain.
// docusaurus-theme-search-algolia/src/index.js

let compiledOpenSearchTemplate = eta.compile(openSearchTemplate.trim())

function renderOpenSearchTemplate(data) {
  return compiledOpenSearchTemplate(data, eta.defaultConfig);
}
// docusaurus/src/client/serverEntry.js

let compiledSSRTemplate = eta.compile(ssrTemplate.trim(), {rmWhitespace: true})

function renderSSRTemplate(data) {
  // We can render with eta.defaultConfig, because {rmWhitespace: true} only affects the template
  // during compilation
  return compiledSSRTemplate(data, eta.defaultConfig);
}

@nebrelbug
Copy link
Contributor

Note that we could memoize renderRedirectPageTemplate, renderOpenSearchTemplate, and renderSSRTemplate. This would make the functions automatically return the same result if they were called with the same data.

Since the template functions basically just perform string concatenation, however, I'm not sure this would bring an additional noticeable performance increase.

@slorber
Copy link
Collaborator Author

slorber commented Aug 17, 2020

@nebrelbug

// We don't have to generate this every time because the template won't change
let compiledTemplate = eta.compile(redirectPageTemplate.trim())

function renderRedirectPageTemplate(data: object) {
  return compiledTemplate(data, eta.defaultConfig);
}

this means the template is compiled at plugin initialization time. I don't necessarily want the template to be compiled early in the process, particularly if it's only used in the postBuild step, that's why I use the memoize in code, to compile it lazily, yet cache for further uses.

I don't want to memorize the render() call because it will be very often called with different args, and the memorize cache would grow, consume memory, and mostly produce cache misses.

Does this PR looks good to merge or do you see things to improve?


function renderSSRTemplate(data) {
const compiled = getCompiledSSRTemplate();
return compiled(data, {...eta.defaultConfig, ...customEtaConfig});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can change this to return compiled(data, eta.defaultConfig);

import {memoize} from 'lodash';

const customEtaConfig = {
name: 'ssr-template',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove the name option: it's used only for Eta's built-in caching inside render() and compile(), which we're bypassing to render.

@nebrelbug
Copy link
Contributor

That makes sense. I just suggested a few additional changes, then I think it looks good! 👍

@slorber
Copy link
Collaborator Author

slorber commented Aug 17, 2020

Thanks, just made the last changes and will merge asap

@slorber slorber merged commit d17df95 into master Aug 17, 2020
@yangshun yangshun deleted the slorber/eta-compile branch August 26, 2020 02:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed Signed Facebook CLA pr: performance This PR does not add a new behavior, but existing behaviors will be more memory- / time-efficient.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants