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

Speed up tpl #11351

Merged
merged 7 commits into from Jan 8, 2024
Merged

Speed up tpl #11351

merged 7 commits into from Jan 8, 2024

Conversation

greed42
Copy link
Contributor

@greed42 greed42 commented Sep 16, 2022

What this PR does / why we need it:

This dramatically improves the performance of the tpl function, resolving the issues described in #8002. Our typical charts go from 2-3 minute renders to 1-2 seconds. A particularly pathological one went from 37 minutes to 23 seconds.

It uses a clone of the invoking Go Template instance, rather that re-building everything from scratch, to isolate the effects of parsing the tpl text from the calling context. To allow includes of defines from the tpl text to continue to work, it also re-defines include within the clone so it can close over the clone itself.

Special notes for your reviewer:

The only user-noticeable change should be to performance: actual chart output is unaffected by this change. All tests are unaffected.

If applicable:

  • this PR contains documentation
  • this PR contains unit tests
  • this PR has been tested for backwards compatibility

- Use a clone of the current Template instead of re-creating everything from scratch
- Needs to inject `include` so any defines in the tpl text can be seen.

Signed-off-by: Graham Reed <greed@7deadly.org>
@pull-request-size pull-request-size bot added the size/M Denotes a PR that changes 30-99 lines, ignoring generated files. label Sep 16, 2022
@yxxhero
Copy link
Member

yxxhero commented Sep 23, 2022

@greed42 good job.

@StephanDollberg
Copy link

@yxxhero can this be triaged/merged/looked at?

Seems like a massive benefit for a little change.

@yxxhero
Copy link
Member

yxxhero commented Oct 13, 2022

@bacongobbler @hickeyma @joejulian @scottrigby could you have free time to review this feature? this is a good change. Thanks very much.

@joejulian
Copy link
Contributor

How does this differ from #8371?

@yxxhero
Copy link
Member

yxxhero commented Oct 15, 2022

@joejulian main logic is the same. I think we should discuss the PR in the meeting. WDYT?

@joejulian
Copy link
Contributor

Sounds good

@yxxhero
Copy link
Member

yxxhero commented Oct 15, 2022

@joejulian my English is not good. So looking forward to your discussion. Thanks very much.

@joejulian
Copy link
Contributor

Actually, no meetings the rest of October.

If one isn't any better than the other, we should just add the older one to the next minor release milestone and close the newer.

@yxxhero
Copy link
Member

yxxhero commented Oct 15, 2022

@joejulian I'm not familiar with this core logic of tpl. So I can't pick out which is better.

@joejulian
Copy link
Contributor

@greed42 @diafour any comment about the difference of these two PRs?

@greed42
Copy link
Contributor Author

greed42 commented Oct 17, 2022

#8371 permutes the existing code less, so is trivially better on that measure. It's also better for DRY-ness.

Mine also has a bug with define inside tpl that then calls tpl and includes that define, so #8371 is also better there.

@greed42
Copy link
Contributor Author

greed42 commented Oct 18, 2022

Though #8371 has an infinite recursion bug with a case that involves .Files.Get which my approach (just not re-using the filename:string render interface) doesn't suffer. I haven't gotten it isolated yet.

@greed42
Copy link
Contributor Author

greed42 commented Oct 18, 2022

.Files.Get is a possible red-herring for #8371; it's an empty string to tpl that's the problem, basically:

{{ tpl "" . }}

Except that PR looks to do .New(name).Parse(tpl) which I would expect to not suffer from that problem... except the sign you haven't done .New(name) is that:

 {{ tpl `{{ "" }` . }}

also recurses and it doesn't.

@greed42
Copy link
Contributor Author

greed42 commented Oct 18, 2022

OK I've worked out that the difference between t.Execute() and t.ExecuteTemplate() is critical to how empty behaves. The latter will let "earlier" definitions leak through, as t.Parse() will always grab the previous value for a given name for the named association... while the "plain" template t.Execute() uses will see the empty text.

I think that means it is impractical to share Execute code between tpl and regular manifest expansion. tpl needs to be a special case, like include is a special case.

I will see if I can get some tests for the various edge cases I've observed with my chart library regression suite, and set them up as a separate PR so that this PR does not have additions for tests that cover existing behaviour.

associate: https://cs.opensource.google/go/go/+/refs/tags/go1.17.13:src/text/template/template.go;l=227
grab the previous if empty: https://cs.opensource.google/go/go/+/refs/tags/go1.17.13:src/text/template/template.go;l=138

@joejulian
Copy link
Contributor

Based on that analysis, I'll add #8371 to the v3.11.0 milestone and hope that the core maintainers have time to get it reviewed and merged.

As we're using `t.Clone()` we already get the right 'references'.

Signed-off-by: Graham Reed <greed@7deadly.org>
Signed-off-by: Graham Reed <greed@7deadly.org>
@greed42
Copy link
Contributor Author

greed42 commented Oct 20, 2022

Based on that analysis, I'll add #8371 to the v3.11.0 milestone and hope that the core maintainers have time to get it reviewed and merged.

I've written #11456 to add tests for existing tpl behaviour. They show an expected change from #8371, but also show an infinite recursion problem with an empty string which I don't think can be solved while still using (t) ExecuteTemplate(...). Only the "implied" template, the one that's run with (t) Execute(...), can be cleared out with (t) New(name) and remain empty with (t) Parse("").

This PR will also need some more commits if #11456 is merged.

@joejulian
Copy link
Contributor

I believe you're saying that #11456 is the better solution now? I'm happy to switch which PR's in the milestone. I can't duplicate the issue so I have to count on you folks that see the problem to guide me here.

@greed42
Copy link
Contributor Author

greed42 commented Oct 24, 2022

I believe you're saying that #11456 is the better solution now? I'm happy to switch which PR's in the milestone. I can't duplicate the issue so I have to count on you folks that see the problem to guide me here.

No; #11456 is just tests for existing behaviour. It doesn't fix anything. #11351 (this one) is the fix I would now recommend.

The fault in #8371 is with this manifest or anything containing:

---
kind: Test
data: {{ tpl "" . }}

Or other define-only things like:

---
kind: Test
data: {{ tpl `{{define "foo"}}test{{end}}`  . }}

You get a stack overflow, as the empty template never becomes the template ExecuteTemplate sees, and so:

% bin/helm template test
runtime: goroutine stack exceeds 1000000000-byte limit
runtime: sp=0xc0eb46a380 stack=[0xc0eb46a000, 0xc10b46a000]
fatal error: stack overflow
...

@joejulian joejulian added this to the 3.11.0 milestone Oct 27, 2022
@mattfarina
Copy link
Collaborator

I like the speedup. We'll need to take a very careful look at this change as there is potential to break existing charts.

@pull-request-size pull-request-size bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Jul 31, 2023
@greed42
Copy link
Contributor Author

greed42 commented Jul 31, 2023

It works for me. I don't have anything that takes more than 2 seconds, anyway, so I can't see the improvement, but there's enough feedback to give me confidence that it works for others.

Thanks. The new tests will fail after merging in main, so I've pushed a merge and the appropriate test changes to this branch.

@stavros-k
Copy link

stavros-k commented Aug 1, 2023

Here is some numbers from someone what has something big :)

/tmp/helm/bin/helm version   
version.BuildInfo{Version:"v3.12+unreleased", GitCommit:"b261a1b1bee93343cf9fe92335d3f1ccf3e24558", GitTreeState:"clean", GoVersion:"go1.20.6"}

time /tmp/helm/bin/helm template . > /dev/null
/tmp/helm/bin/helm template . > /dev/null  0,51s user 0,04s system 159% cpu 0,344 total
helm version
version.BuildInfo{Version:"v3.12.2", GitCommit:"1e210a2c8cc5117d1055bfaa5d40f51bbc2e345e", GitTreeState:"clean", GoVersion:"go1.20.5"}

time helm template . > /dev/null 
helm template . > /dev/null  22,30s user 0,45s system 147% cpu 15,452 total

Considering that a test suite of tests can include up to 800 apps. It will same A TON of time!
(https://github.com/truecharts/charts)

Unit tests did not have any speed increase, but should be an indicator that it does not break something :)

Helm built from this PR:

Charts:              1 passed, 1 total
Test Suites:   133 passed, 133 total
Tests:            805 passed, 805 total
Snapshot:         0 passed, 0 total
Time:        8m26.098330718s

Helm v3.12.2

Charts:             1 passed, 1 total
Test Suites:  133 passed, 133 total
Tests:           805 passed, 805 total
Snapshot:        0 passed, 0 total
Time:        8m39.303807066s

@yxxhero
Copy link
Member

yxxhero commented Aug 5, 2023

speed up!!!!

@joejulian joejulian assigned mattfarina and scottrigby and unassigned joejulian Aug 16, 2023
@yxxhero
Copy link
Member

yxxhero commented Aug 23, 2023

@joejulian would you mind to talk the PR in the dev meeting?

@joejulian joejulian added this to the 3.13.0 milestone Aug 30, 2023
@starwarsfan
Copy link

What's the current state here? Will this be fixed with 3.13?

@mattfarina mattfarina modified the milestones: 3.13.0, 3.14.0 Sep 25, 2023
@starwarsfan
Copy link

As 3.13.0 is released, the fix did not make it into it, right?

@Kuzmenko-Pavel
Copy link

We look forward to speeding up!!!

@HrechykhinVlad
Copy link

Please speed up, we need those changes. Thank you!

@Lokker29
Copy link

Lokker29 commented Oct 6, 2023

We really need this improvement. Please, merge it!

@Mixxxxerr
Copy link

Need this asap. Merge pls

@DaniilOmelianenko
Copy link

Hey! This really cool! Speed up it, please.

@hrekov
Copy link

hrekov commented Oct 9, 2023

Looks cool! Speed it up, please.

@FlamingFlowair
Copy link

No news on this, is it still under review ? we're almost 3 months after the first approval.
Hope this gets merged soon

Copy link

@Ornias1993 Ornias1993 left a comment

Choose a reason for hiding this comment

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

Nicely structured and documented.
Speed improvement is crucial for bigger repositories and charts.

LGTM.

@Ornias1993
Copy link

@scottrigby and @mattfarina Can we get a final review from you two as well?

It seems most issues including previous upstream blockers have been dealth with and this PR would give a significant boost to making Helm more usefull for projects with more complex requirements relying on heavy tpl usage.

@swythan
Copy link

swythan commented Dec 12, 2023

Any update on getting this merged?

Copy link
Contributor

@sabre1041 sabre1041 left a comment

Choose a reason for hiding this comment

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

LGTM

@sabre1041 sabre1041 merged commit 77d54d7 into helm:main Jan 8, 2024
5 checks passed
@yxxhero
Copy link
Member

yxxhero commented Jan 8, 2024

Cool.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Has One Approval This PR has one approval. It still needs a second approval to be merged. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet