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
A few frozen string and symbol optimizations #8177
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. |
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.