From 83c385719d95c178790e52f78e2b247d1476eb5e Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Mon, 20 Nov 2023 13:48:34 +0100 Subject: [PATCH] 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| ``` --- benchmark/string_dup.yml | 7 +++++++ string.c | 13 +++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 benchmark/string_dup.yml diff --git a/benchmark/string_dup.yml b/benchmark/string_dup.yml new file mode 100644 index 0000000000..90793f9f2a --- /dev/null +++ b/benchmark/string_dup.yml @@ -0,0 +1,7 @@ +prelude: | + # frozen_string_literal: true +benchmark: + uplus: | + +"A" + dup: | + "A".dup diff --git a/string.c b/string.c index 41641c67e8..2f69ab1adb 100644 --- a/string.c +++ b/string.c @@ -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);