Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[perf] Expression caching #1473

Open
Open
@JuroOravec

Description

@JuroOravec

Context

When a template has an expression{{ ... }} or a tag{% ... %}, we can treat it as a function - same input should return same output.

I'm currently preparing the template AST parser. And in order to support#1083 in the future, I'll be adding a feature to the AST parser to keep track of which context variables were used in an expression or a tag.

So e.g. in this template:

<div>  {{ title }}</div>{%component"user_settings"settings=settingsuser=usertitle="{{ title }}: {{ user.first_name }} {{ user.last_name }}"/%}

We have:

  1. Expression that uses 1 variable -title
  2. user_settings component tag that uses 3 variables -settings,user, andtitle.

What#1083 does is that if these variables are marked asConst, then we can replace these expressions and tags with static values. Saving us from having to re-compute these values in the future.

E.g. iftitle was statically defined asConst("User settings"), then after the first render we'd replace{{ title }} with the actual value:

<div>  User settings</div>{%component"user_settings"settings=settingsuser=usertitle="User settings: {{ user.first_name }} {{ user.last_name }}"/%}

But expressions like{{ user.first_name }} can never be static - they depend on the logged-in user.

Yet, right now we re-compute the value of{{ user.first_name }} every time, even if I hit this page with the same user hundred times.

Proposal

With the upcoming AST, if we store which variables were used inside of a tag or expression, we could use that to cache the results of expressions, and avoid re-computing them.

It would look roughly like this (pseudocode):

classExpr:defrender(self,context):expr_str=self.raw# e.g. `{{ user.first_name }}`used_vars=self.used_vars# ["user"]expr_data= {key:context[key]forkeyinused_vars}# {"user": User<id=1>}cache_key=expr_str+"::"+hash(frozenset(var_values.items()))component_context=get_closest_component(context)ifcomponent_contextandcomponent_context.expressions_cache:expr_cache=component_context.expressions_cache# E.g. caches["djc-expressions"]else:expr_cache=None# Case: This expr was already rendered with this inputifexpr_cacheandcache_keyinexpr_cache:returnexpr_cache[cache_key]# Case: First time rendering this expr with this input, cache itelifexpr_cacheisnotNone:result=super().render(context)expr_cache[cache_key]=resultreturnresult# Case: NOT caching the expression resultelse:result=super().render(context)returnresult

Tho I'm concerned that caching every single expression could be actually detrimental as it might introduce too much overhead.

A middle ground could be to walk down the template nodes, and for each one check which variables it uses in itself and it's body.

To achieve that , each AST node should report not only which variables it uses in its tag ({% ... %}), but also what variables it uses in it's body.

So imagine I walk down the following template:

<div>{# <-- 1 #}{%component"table"headers=table_headersrows=rows%}{# <-- 2 #}{%fill"pagination"%}{# <-- 3 #}{%component"button"%}{# <-- 4 #}           Edit user: {{ user.username }}{# <-- 5 #}{%endcomponent%}{%endfill%}{%endcomponent%}</div>
  1. I start at the level of the entire template. I see that the entire body uses variablestable_headers,rows,anduser

    Let's say thattable_headers anduser variables are the same as the previous render, butrows has new data. Nothing we can do on the template-level.

  2. Then we reach{% component "table" %}. It again uses the same variables,table_headers,rows, anduser, so nothing to do.

  3. Next we visit the{% fill "pagination" %} node. This already contains only 1 variable -user.

    Since we had previously rendered this section with the sameuser entry, we can access the cached result for the entire{% fill "pagination" %} section.

  4. Thus, we managed to skip the rendering of{% component "button" %} and{{ user.username }}.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions


      [8]ページ先頭

      ©2009-2025 Movatter.jp