-
Notifications
You must be signed in to change notification settings - Fork 443
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
Add configuration and support for freezing string literals in compiled templates #1884
Add configuration and support for freezing string literals in compiled templates #1884
Conversation
500a675
to
8085fed
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You raise a good point about delegating back to Rails' own config for this. It would be possible to inherit from the Rails default from within the defaults
method since it isn't called until Rails is ready to receive that. I'd recommend doing so and updating the docs accordingly.
lib/view_component/compiler.rb
Outdated
if frozen_string_literal | ||
component_class.class_eval("# frozen_string_literal: true\n#{source}", template.path, template.lineno - 1) | ||
else |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SimpleCov says this line's not tested, just FYI!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for catching that, added a test in b15f939 which should cover the inline template case.
I tried some quick testing and it looks like |
Ah, that probably does come ahead of, say, |
The other option I thought about was defaulting to def render_in(view_context, &block)
if ViewComponent::Base.config.frozen_string_literal == :rails
self.class.compile(raise_errors: true, frozen_string_literal: Rails.application.config.action_view.frozen_string_literal)
else
self.class.compile(raise_errors: true, frozen_string_literal: ViewComponent::Base.config.frozen_string_literal)
end
# ...
end |
Sorry for the repeated comments, I tried another approach in 60a92d2 to make use of the |
fff7c47
to
60a92d2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is awesome! I wonder if it'd be possible to write a test to ensure that the value is inherited from Rails' config?
I tried, and it got a little bit weird in 385cb87. Open to suggestions on how to structure it, which test cases are important, etc. I'm also getting a failure in |
That seems like the right way to do it to me! Awesome work figuring that out – I would never have guessed how to do it myself 🤯 I'm hoping it's just a flaky spec failure, so I've rerun the job. If this goes green, I think we can approve it! |
Thanks! For the spec failure, I merged main to see if that would help, but main seems to be failing too. It looks like there may be a couple issues in the
But the newer failure appears to be related to gem versions. |
c62acff
to
d7cfafa
Compare
Looks like updating to Rails 7.1.1 here in the Primer tests fixes the gem issue |
@mitchellhenke I had the same issue by merging main back into your branch your issues should be fixed |
d7cfafa
to
013a2bd
Compare
Thanks, rebased, and it looks like CI is happy! I can squash commits or whatever is preferred prior to merging if needed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow, great work @mitchellhenke, thank you!
Unfortunately I'm not seeing any performance improvements when running the ViewComponent benchmarks. It looks like the ERB template engine already adds .freeze
onto literal strings, but even if I remove those calls I still see no change in performance. I'm not against this change at all, and I think it could be beneficial for other template engines like HAML, Slim, etc, but I don't understand why there's no perf impact. Any ideas here?
Thanks! I think most of the benefit will be in memory usage, with maybe a tiny performance gain, depending on the application and templates. I started a memory-benchmark branch to try to set up both a memory performance benchmark and a performance benchmark of rendering with and without freezing string literals. A minimal template that where a significant portion is made up of string literals in Ruby shows some memory improvements and an insignificant "improvement" in performance. The example template is intended to try to get some of the benefit of string freezing by putting a literal in a Ruby block. The template and results are below, but I think it's worth trying a variety to see the impact. In running performance benchmarks. I'm also not sure I've set up the benchmarks appropriately.
|
To add a bit, I totally understand if the complexity is not worth the improvement 🙂 |
b98ab8b
to
95a6c8f
Compare
@mitchellhenke ahh ok that makes sense 👍 I thought we would see a significant speed-up as well, since Ruby would have to execute fewer
Yeah, I wonder if an app like GitHub will see memory savings over time with this change. My guess is no because the ERB templating engine already freezes the majority of literal strings in a given template.
🤔 I'm chatting with another maintainer about this. It's really not too bad complexity-wise, so I'm inclined to accept the PR. Will circle back today. |
Ok @mitchellhenke the consensus is we'd like to see some numbers from a real-world app before merging. Is that feasible for you? |
f35aa5c
to
ec74a77
Compare
@camertron I can definitely try! I found some issues that prevented the string freezing from happening in production/eager load environments and applied a patch in fba786e. I suspect applications doing more than static content may not be able to see it on the speed performance side (depending on their usage of view_component). A simple
A more complex repo I tested on didn't have a significant difference (though it was a very very small improvement). |
fba786e
to
1e64159
Compare
@camertron I think the best way to get actual production metrics is to include it in a release as experimental maybe |
Happy to make adjustments to the configuration, just let me know! |
1e64159
to
9f9a926
Compare
9f9a926
to
8649a97
Compare
Wanted to check in on this quick 🙂 I am happy to make changes to clarify this as more experimental and change the default to disabling if that's preferable. |
@mitchellhenke thanks for taking the time to dig into this idea! Given how intrusive the change is, I'm hesitant to merge it even as an experimental flag given the current (very thorough ❤️) benchmark data. Would you be willing to run your fork in production to get a real-world impact of the change? |
@joelhawksley I'll see if I can. I've got a little bit of prior work to take care of first (we're a bit behind on view_component versions already and I can't guarantee a production test, but I'd like to. Failing real production, I can try a more "prod-like" environment. |
8649a97
to
de697cd
Compare
0d91b95
to
9f746c2
Compare
9f746c2
to
6d12b09
Compare
Co-authored-by: Simon Fish <[email protected]>
6d12b09
to
038e77f
Compare
@mitchellhenke any updates from your end ? |
@reeganviljoen sorry for the delay! I did a test in a deployed environment and the difference in performance is not significant. I think the potential for memory reduction may be helpful in the cases where there many or large string constants in the templates. I don't see much of that use case in my experience, so I'm guessing the changes here are not worthwhile. I am glad to close this PR, but appreciate you all taking the time to provide feedback and evaluate it. |
@joelhawksley any comments ? |
I don't think it's worth the trouble at this point. Thank you for the exploratory work @mitchellhenke ❤️ |
What are you trying to accomplish?
This is a proof-of-concept of closing #1883 (if it's helpful for discussion there)
What approach did you choose and why?
I mostly followed the structure of the Rails pull request. I'm not super familiar with templating engines or their implementation, so I don't have a lot to draw on unfortunately.
Anything you want to highlight for special attention from reviewers?
The configuration feels kind of weird. Rails has a configuration for this, and it might be reasonable to consider inheriting that configuration rather than have a separate one for
view_component
?I suspect most people enabling it in Rails (it defaults to false) would want to have it for their
view_component
templates, but it does potentially break applications if they are mutating strings.