Skip to content

Specialize String#dup#8952

Merged
byroot merged 1 commit intoruby:masterfrom
Shopify:faster-str-lit-dup
Nov 20, 2023
Merged

Specialize String#dup#8952
byroot merged 1 commit intoruby:masterfrom
Shopify:faster-str-lit-dup

Conversation

@byroot
Copy link
Member

@byroot byroot commented Nov 20, 2023

String#+@ is 2-3 times faster than String#dup because it can directly go through rb_str_dup instead of using the generic much slower rb_obj_dup.

This fact led to the existence of the ugly Performance/UnfreezeString rubocop performance rule that encourage users to rewrite the much more readable and convenient "foo".dup into the ugly (+"foo").

Let's make that rubocop rule useless.

compare-ruby: ruby 3.3.0dev (2023-11-20T02:02:55Z master https://github.com/ruby/ruby/commit/701b0650de8a5b1436ce1abc65e0fcc2be480c2d) [arm64-darwin22]
last_commit=[ruby/prism] feat: add encoding for IBM865 (https://github.com/ruby/prism/pull/1884)
built-ruby: ruby 3.3.0dev (2023-11-20T12:51:45Z faster-str-lit-dup https://github.com/ruby/ruby/commit/6b745bbc5df41fea69154216a45f0275d17e4de2) [arm64-darwin22]
warming up..

|       |compare-ruby|built-ruby|
|:------|-----------:|---------:|
|uplus  |     16.312M|   16.332M|
|       |           -|     1.00x|
|dup    |      5.912M|   16.329M|
|       |           -|     2.76x|

`String#+@` is 2-3 times faster than `String#dup` because it can
directly go through `rb_str_dup` instead of using the generic
much slower `rb_obj_dup`.

This fact led to the existance of the ugly `Performance/UnfreezeString`
rubocop performance rule that encourage users to rewrite the much
more readable and convenient `"foo".dup` into the ugly `(+"foo")`.

Let's make that rubocop rule useless.

```
compare-ruby: ruby 3.3.0dev (2023-11-20T02:02:55Z master 701b065) [arm64-darwin22]
last_commit=[ruby/prism] feat: add encoding for IBM865 (ruby/prism#1884)
built-ruby: ruby 3.3.0dev (2023-11-20T12:51:45Z faster-str-lit-dup 6b745bb) [arm64-darwin22]
warming up..

|       |compare-ruby|built-ruby|
|:------|-----------:|---------:|
|uplus  |     16.312M|   16.332M|
|       |           -|     1.00x|
|dup    |      5.912M|   16.329M|
|       |           -|     2.76x|
```
@akhilgkrishnan
Copy link

@byroot This is really great 💯

@XrXr XrXr deleted the faster-str-lit-dup branch November 20, 2023 18:00
headius added a commit to jruby/jruby that referenced this pull request Jan 5, 2024
koic added a commit to koic/lrama that referenced this pull request May 28, 2024
Starting with Ruby 3.4, there is a gradual plan to freeze strings:
https://bugs.ruby-lang.org/issues/20205#note-35

Using Ruby 3.4dev with code prior to this PR results in the following warning:

```console
$ cd path/to/ruby/lrama
$ ruby -v
ruby 3.4.0dev (2024-05-27T01:45:38Z master 5853a38043) [x86_64-darwin23]
$ RUBYOPT=-W:deprecated bundle exec rake
(snip)

/Users/koic/src/github.com/ruby/lrama/lib/lrama/output.rb:74: warning: literal string will be frozen in the future
```

This warning suggests that applying the frozen string magic comment will lead to `FrozenError`.
So, this indicates that the future behavior of (default frozen) string will produce `FrozenError`.

This PR applies the frozen string magic comment to detect `FrozenError` and uses `String#dup` to prevent it:
https://gist.github.com/fxn/bf4eed2505c76f4fca03ab48c43adc72#ruby-34

This ensures compatibility for the future.

NOTE: From Ruby 3.3+, there is no speed difference between `String#+@` and `String#dup`. For readability, `String#dup` has been chosen:
ruby/ruby#8952
koic added a commit to koic/lrama that referenced this pull request May 28, 2024
Starting with Ruby 3.4, there is a gradual plan to freeze strings:
https://bugs.ruby-lang.org/issues/20205#note-35

Using Ruby 3.4dev with code prior to this PR results in the following warning:

```console
$ cd path/to/ruby/lrama
$ ruby -v
ruby 3.4.0dev (2024-05-27T01:45:38Z master 5853a38043) [x86_64-darwin23]
$ RUBYOPT=-W:deprecated bundle exec rake
(snip)

/Users/koic/src/github.com/ruby/lrama/lib/lrama/output.rb:74: warning: literal string will be frozen in the future
```

This warning suggests that applying the frozen string magic comment will lead to `FrozenError`.
So, this indicates that the future behavior of (default frozen) string will produce `FrozenError`.

This PR applies the frozen string magic comment to detect `FrozenError` and uses `String#dup` to prevent it:
https://gist.github.com/fxn/bf4eed2505c76f4fca03ab48c43adc72#ruby-34

This ensures compatibility for the future.

NOTE: From Ruby 3.3+, there is no speed difference between `String#+@` and `String#dup`. For readability, `String#dup` has been chosen:
ruby/ruby#8952
koic added a commit to koic/lrama that referenced this pull request May 28, 2024
Starting with Ruby 3.4, there is a gradual plan to freeze strings:
https://bugs.ruby-lang.org/issues/20205#note-35

Using Ruby 3.4dev with code prior to this PR results in the following warning:

```console
$ cd path/to/ruby/lrama
$ ruby -v
ruby 3.4.0dev (2024-05-27T01:45:38Z master 5853a38043) [x86_64-darwin23]
$ RUBYOPT=-W:deprecated bundle exec rake
(snip)

/Users/koic/src/github.com/ruby/lrama/lib/lrama/output.rb:74: warning: literal string will be frozen in the future
```

This warning suggests that applying the frozen string magic comment will lead to `FrozenError`.
So, this indicates that the future behavior of (default frozen) string will produce `FrozenError`.

This PR applies the frozen string magic comment to detect `FrozenError` and uses `String#dup` to prevent it:
https://gist.github.com/fxn/bf4eed2505c76f4fca03ab48c43adc72#ruby-34

This ensures compatibility for the future.

NOTE: From Ruby 3.3+, there is no speed difference between `String#+@` and `String#dup`. For readability, `String#dup` has been chosen:
ruby/ruby#8952
toshimaru pushed a commit to toshimaru/rubocop-rails_config that referenced this pull request Jan 8, 2026
This change removes the Performance/UnfreezeString cop from the RuboCop configuration to align with the upstream change in Rails PR #50113. After the change in ruby/ruby#8952, String#dup also having the same performance of String#+@. This makes the cop obsolete.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants