crystal icon indicating copy to clipboard operation
crystal copied to clipboard

Inline constants in the generated code

Open mattrbeck opened this issue 3 years ago • 11 comments

Discussion

What aspect of the language would you like to see improved?

I'm curious whether we can propagate known constants in Crystal to the emitted LLVM so they can be stored as data.

What are the reasons?

It should ideally result in more efficient code and less strain on the garbage collector.

Include practical examples to illustrate your points.

The specific example that made me think of this is that I want the UI of my app to provide hints for keyboard shortcuts. For example:

MODIFIER_KEY_STR = {% if flag?(:darwin) %} "Cmd" {% else %} "Ctrl" {% end %}
{"Fullscreen", "{MODIFIER_KEY_STR}+F"}

Today, this generates a call to String.interpolation, which allocates a new string on every call. This also happens when concatenating the strings with +. Since MODIFIER_KEY_STR is a constant, I'd hope that we could concatenate the strings at compile-time and store the resulting constant in the binary.

Optionally add one (or more) proposals to improve the current situation.

I have no idea what this would look like in the compiler.

mattrbeck avatar Sep 24 '22 17:09 mattrbeck

I had a branch for that but I didn't think it was that important. I lost it but I can redo it.

asterite avatar Sep 24 '22 17:09 asterite

To be fair, I haven't compared measured the impact of performance. I don't actually know whether it's significant. My app barely uses the GC today though, so this topic came to mind when writing the code. It's a few hundred allocations per second that probably don't need to exist

mattrbeck avatar Sep 24 '22 17:09 mattrbeck

String.interpolation is not guaranteed to be a constant expression, since it is only a regular method. It does not even exist in an empty prelude. Instead you could do the following to force constant evaluation:

{% begin %}
  MODIFIER_KEY_STR = ...
{% end %}
{{ "#{MODIFIER_KEY_STR.id}+F" }}

HertzDevil avatar Sep 24 '22 19:09 HertzDevil

I think the proposal here is for the compiler to notice that the interpolations interpolates a constant, so it could very well interpolate things at compile time.

I actually had a branch with this working, I just didn't sent it to avoid overwhelming the team with such PRs at that time.

asterite avatar Sep 24 '22 23:09 asterite

Doing so involves running an arbitrary amount of non-macro code during compilation, because there is no bound on the #to_s method's complexity. At this time I wouldn't worry about such an optimization or whitelist certain interpolations when the {{ "#{}" }} workaround suffices.

HertzDevil avatar Sep 24 '22 23:09 HertzDevil

@HertzDevil This should be limited to constants of type String (or perhaps some primitive types as well?). Basically, the literal expander would just resolve constant values and if it's of type String, combine it with neighbouring string literals.

But since constants are not immutable, this wouldn't work for arbitrary types anyway, because the constant could have a different value each time it is used in string expansion (simple example: it's an array and elements get added and removed at runtime).

straight-shoota avatar Sep 25 '22 12:09 straight-shoota

I think there's a misunderstanding here. This is about constants whose value is a string literal. Just that.

asterite avatar Sep 25 '22 13:09 asterite

MODIFIER_KEY_STR's initializer is a MacroIf that only gets expanded in the main phase, rather than a StringLiteral. The two are not quite the same.

HertzDevil avatar Sep 25 '22 13:09 HertzDevil

The idea would be to try to resolve that before expanding the interpolation. I'll send a PR later during this week.

asterite avatar Sep 25 '22 14:09 asterite

Here's the PR: https://github.com/crystal-lang/crystal/pull/12524/files

asterite avatar Sep 25 '22 20:09 asterite

#12671 was eventually reverted in https://github.com/crystal-lang/crystal/pull/14155 due to https://github.com/crystal-lang/crystal/issues/14150.

straight-shoota avatar Jan 03 '24 10:01 straight-shoota