[#114703] [Ruby master Bug#19875] Ruby 2.7 -> 3.1 Performance regression in String#count — "iz (Illia Zub) via ruby-core" <ruby-core@...>

Issue #19875 has been reported by iz (Illia Zub).

18 messages 2023/09/12

[#114774] [Ruby master Feature#19884] Make Safe Navigation Operator work on classes — "p8 (Petrik de Heus) via ruby-core" <ruby-core@...>

Issue #19884 has been reported by p8 (Petrik de Heus).

13 messages 2023/09/15

[#114796] [Ruby master Feature#19889] Let `Kernel.#require` search for files relative to the current working directory for non ./, ../ relative paths — "sawa (Tsuyoshi Sawada) via ruby-core" <ruby-core@...>

Issue #19889 has been reported by sawa (Tsuyoshi Sawada).

6 messages 2023/09/18

[#114803] [Ruby master Bug#19890] File#realine(chomp: true) slower/more allocations than readline.chomp! — "segiddins (Samuel Giddins) via ruby-core" <ruby-core@...>

Issue #19890 has been reported by segiddins (Samuel Giddins).

12 messages 2023/09/18

[#114817] [Ruby master Bug#19892] Build failure with 8f1b688177 — "vo.x (Vit Ondruch) via ruby-core" <ruby-core@...>

Issue #19892 has been reported by vo.x (Vit Ondruch).

8 messages 2023/09/19

[#114915] [Ruby master Feature#19905] Introduce `Queue#peek` — "hi@... (Joao Fernandes) via ruby-core" <ruby-core@...>

Issue #19905 has been reported by hi@joaofernandes.me (Joao Fernandes).

8 messages 2023/09/28

[ruby-core:114779] [Ruby master Bug#19875] Ruby 3.0 -> 3.1 Performance regression in String#count

From: "Freaky (Thomas Hurst) via ruby-core" <ruby-core@...>
Date: 2023-09-15 12:32:18 UTC
List: ruby-core #114779
Issue #19875 has been updated by Freaky (Thomas Hurst).





nobu (Nobuyoshi Nakada) wrote in #note-7:



> These are all generated by the same compiler?



Yes - FreeBSD clang version 14.0.5.  I also try 15.0.7 to no effect.



gcc 12.2.0 performs much better, though it too has a slightly more modest p=
erformance regression from 4001:



```

  './ruby.gcc12.revert test.rb' ran

    1.28 =B1 0.01 times faster than './ruby.clang.revert test.rb'

    1.61 =B1 0.01 times faster than './ruby.gcc12.master test.rb'

    4.38 =B1 0.04 times faster than './ruby.clang.master test.rb'

```



The obvious solution works for both:



```

diff --git string.c string.c

index deeed4a12a..70644b2338 100644

--- string.c

+++ string.c

@@ -8455,6 +8455,13 @@ rb_str_count(int argc, VALUE *argv, VALUE str)

             s =3D RSTRING_PTR(str);

             if (!s || RSTRING_LEN(str) =3D=3D 0) return INT2FIX(0);

             send =3D RSTRING_END(str);

+            if (RSTRING_LEN(str) < INT_MAX) {

+                i =3D 0;

+                while (s < send) {

+                    if (*(unsigned char*)s++ =3D=3D c) i++;

+                }

+                return INT2NUM(i);

+            }

             while (s < send) {

                 if (*(unsigned char*)s++ =3D=3D c) n++;

             }

```



```

  './ruby.gcc12.fix test.rb' ran

    1.04 =B1 0.01 times faster than './ruby.gcc12.revert test.rb'

    1.32 =B1 0.01 times faster than './ruby.clang.fix test.rb'

    1.33 =B1 0.02 times faster than './ruby.revert test.rb'

    1.68 =B1 0.01 times faster than './ruby.gcc12.master test.rb'

    4.57 =B1 0.03 times faster than './ruby.master test.rb'

```



Oh.  And you know what this reminds me of?  That [one time](https://github.=
com/Freaky/fast-bytecount) I ported Rust's bytecount crate to C.



```

  './ruby.bytecount test.rb' ran

    3.73 =B1 0.08 times faster than './ruby.gcc12.fix test.rb'

    3.87 =B1 0.08 times faster than './ruby.gcc12.revert test.rb'

    4.94 =B1 0.10 times faster than './ruby.clang.fix test.rb'

    4.96 =B1 0.11 times faster than './ruby.revert test.rb'

    6.27 =B1 0.13 times faster than './ruby.gcc12.master test.rb'

   17.05 =B1 0.36 times faster than './ruby.master test.rb'

```



Who needs a compiler to vectorise for you when you can just ... copy what s=
omeone smarter than you did.



----------------------------------------

Bug #19875: Ruby 3.0 -> 3.1 Performance regression in String#count

https://bugs.ruby-lang.org/issues/19875#change-104621



* Author: iz (Illia Zub)

* Status: Feedback

* Priority: Normal

* ruby -v: 3.2.2

* Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN

----------------------------------------

`String#count` became slower since Ruby 3.1. Originally found by `@Freaky`:=
 https://github.com/ruby/ruby/pull/4001#issuecomment-1714779781



Compared using the [`benchmark-driver` gem](https://github.com/benchmark-dr=
iver/benchmark-driver).



```

$ benchmark-driver tmp/string_count_benchmark_driver.yml --rbenv '3.1.1;3.1=
.4;2.7.2;3.2.2;3.0.6'                                                =20

Calculating -------------------------------------

                          3.1.1       3.1.4       2.7.2       3.2.2       3=
.0.6

               count    465.804     463.741     865.783     462.711     857=
.395 i/s -     10.000k times in 21.468251s 21.563768s 11.550239s 21.611783s=
 11.663235s



Comparison:

                            count

               2.7.2:       865.8 i/s=20

               3.0.6:       857.4 i/s - 1.01x  slower

               3.1.1:       465.8 i/s - 1.86x  slower

               3.1.4:       463.7 i/s - 1.87x  slower

               3.2.2:       462.7 i/s - 1.87x  slower

```



Benchmark:





```yml

$ cat ./tmp/string_count_benchmark_driver.yml=20

loop_count: 10_000

prelude: |

  html =3D "\nruby\n" * 1024 * 1024

benchmark:

  count: html.count($/)

```



---



*Initially, I noticed the difference between `str.count($/)` and `str.lines=
.size` when working on the performance improvement: https://serpapi.com/blo=
g/lines-count-failed-deployments/*



---Files--------------------------------

rb_str_len.fast (31.9 KB)

rb_str_len.slow (34 KB)

revert-4001.patch (1.71 KB)

rb_str_count.S (11.8 KB)





--=20

https://bugs.ruby-lang.org/

 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-c=
ore.ml.ruby-lang.org/

In This Thread