-
-
Notifications
You must be signed in to change notification settings - Fork 939
A few frozen string and symbol optimizations #8177
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
Conversation
Other flags needed to be shifted, which I was afraid of before but now I have no fear.
|
This seemed like a great idea until I realized you can't add new internal variables to a frozen object (or even update them if they have been allocated already). Still feels like there's some value in having fstrings cache common products like symbols and regexp. |
enebo
left a comment
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.
We probably need a comment about how the CR values cannot change.
These values are originally from CRuby, and now hardcoded as part of the Prism parser, so they must continue to have the same values or it will break codranges coming out of Prism. This also makes the value check a hard check rather than an assert since that will help ensure they do not accidentally get changed.
This improves the performance of Integer#to_s for this range of values by a good 50%, based on the attached benchmark.
An FString is a frozen string that has additionally been de-
duplicated and cached. Since such strings are often later
converted to symbols, integers, or floats, the FString class adds
fields to lazily cache those converted values. This helps, for
example, when using APIs that can take either strings or symbols
but need a symbol internally; if the incoming string is an FString
the symbol created from it will be cached and more readily
accessible.
Performance of FString conversions is many times faster with this
change at the cost of all FStrings having three additional
reference fields.
Warming up --------------------------------------
intern normal 6.203M i/100ms
intern fstring 25.811M i/100ms
to_i normal 9.876M i/100ms
to_i fstring 27.629M i/100ms
to_f normal 14.780M i/100ms
to_f fstring 27.612M i/100ms
Calculating -------------------------------------
intern normal 62.499M (± 0.5%) i/s - 316.353M in 5.061824s
intern fstring 274.520M (± 1.0%) i/s - 1.394B in 5.077736s
to_i normal 97.868M (± 1.3%) i/s - 493.824M in 5.046716s
to_i fstring 273.394M (± 1.3%) i/s - 1.381B in 5.053858s
to_f normal 146.627M (± 1.2%) i/s - 739.006M in 5.040805s
to_f fstring 271.105M (± 1.5%) i/s - 1.381B in 5.093658s
This helps cases where an fstring (born frozen and dedup'ed) is used in a symbol API where it must be interned. For example see the benchmark and discussion in jruby#3419.
|
The original FString idea has been implemented with a subclass of RubyString that aggregates lazy fields for symbol, integer, and float. Performance is greatly improved for cases that request those conversions from FStrings. |
This cleans up some object flags in order to start flagging fstrings as such and use that to cache any symbol they might intern.