Specialize String#dup

`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 701b0650de) [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 6b745bbc5d) [arm64-darwin22]
warming up..

|       |compare-ruby|built-ruby|
|:------|-----------:|---------:|
|uplus  |     16.312M|   16.332M|
|       |           -|     1.00x|
|dup    |      5.912M|   16.329M|
|       |           -|     2.76x|
```
This commit is contained in:
Jean Boussier 2023-11-20 13:48:34 +01:00 committed by Jean Boussier
parent 701b0650de
commit 83c385719d
2 changed files with 20 additions and 0 deletions

7
benchmark/string_dup.yml Normal file
View File

@ -0,0 +1,7 @@
prelude: |
# frozen_string_literal: true
benchmark:
uplus: |
+"A"
dup: |
"A".dup

View File

@ -1770,6 +1770,18 @@ rb_str_dup(VALUE str)
return str_duplicate(rb_obj_class(str), str);
}
/* :nodoc: */
VALUE
rb_str_dup_m(VALUE str)
{
if (LIKELY(BARE_STRING_P(str))) {
return str_duplicate(rb_obj_class(str), str);
}
else {
return rb_obj_dup(str);
}
}
VALUE
rb_str_resurrect(VALUE str)
{
@ -12084,6 +12096,7 @@ Init_String(void)
rb_define_method(rb_cString, "freeze", rb_str_freeze, 0);
rb_define_method(rb_cString, "+@", str_uplus, 0);
rb_define_method(rb_cString, "-@", str_uminus, 0);
rb_define_method(rb_cString, "dup", rb_str_dup_m, 0);
rb_define_alias(rb_cString, "dedup", "-@");
rb_define_method(rb_cString, "to_i", rb_str_to_i, -1);