Skip to content
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

Investigate Performance/CollectionLiteralInLoop for Ruby 3.4 #482

Closed
Earlopain opened this issue Dec 25, 2024 · 0 comments · Fixed by #488
Closed

Investigate Performance/CollectionLiteralInLoop for Ruby 3.4 #482

Earlopain opened this issue Dec 25, 2024 · 0 comments · Fixed by #488

Comments

@Earlopain
Copy link
Contributor

Ruby 3.4 added optimizations which I believe are relevant to this cop. See ruby/ruby#12123

Basically specialized instruction handling for [:a, :b].include?(@ivar). I haven't benchmarked yet but maybe this cop can be disabled on ruby 3.4 for most common simple cases if numers are good. Will investigate later.

Earlopain added a commit to Earlopain/rubocop-performance that referenced this issue Jan 25, 2025
… register offenses for `Array#include?` that are optimized directly in Ruby.

On Ruby 3.4 this does no array allocations:
```
require "memory_profiler"

class Foo
  def bar
    Bar.new
  end
end

class Bar
  def baz
  end
end

foo = Foo.new
report = MemoryProfiler.report do
  [1,2].include?(foo.bar.baz)
end.pretty_print
```

Also, this code is simply slower on Ruby 3.4:

```rb
require "benchmark/ips"

local = [301, 302]
val = 301
Benchmark.ips do |x|
  x.report('local') do
    local.include?(val)
  end

  x.report('literal') do
    [301, 302].include?(val)
  end
  x.compare!
end
```
Earlopain added a commit to Earlopain/rubocop-performance that referenced this issue Jan 25, 2025
… register offenses for `Array#include?` that are optimized directly in Ruby.

Since `include?` on arrays are the only instances I ever run across this cop, I think it makes sense to add special handling for this.

On Ruby 3.4 this does no array allocations:
```
require "memory_profiler"

class Foo
  def bar
    Bar.new
  end
end

class Bar
  def baz
  end
end

foo = Foo.new
report = MemoryProfiler.report do
  [1,2].include?(foo.bar.baz)
end.pretty_print
```

Also, this code is simply slower on Ruby 3.4:

```rb
require "benchmark/ips"

local = [301, 302]
val = 301
Benchmark.ips do |x|
  x.report('local') do
    local.include?(val)
  end

  x.report('literal') do
    [301, 302].include?(val)
  end
  x.compare!
end
```

```
$ ruby test.rb
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [x86_64-linux]
Warming up --------------------------------------
               local     1.822M i/100ms
             literal     2.296M i/100ms
Calculating -------------------------------------
               local     18.235M (± 1.6%) i/s   (54.84 ns/i) -     92.906M in   5.096277s
             literal     22.807M (± 1.5%) i/s   (43.85 ns/i) -    114.789M in   5.034289s

Comparison:
             literal: 22806932.0 i/s
               local: 18235340.7 i/s - 1.25x  slower
```
Earlopain added a commit to Earlopain/rubocop-performance that referenced this issue Jan 30, 2025
… register offenses for `Array#include?` that are optimized directly in Ruby.

Since `include?` on arrays are the only instances I ever run across this cop, I think it makes sense to add special handling for this.

On Ruby 3.4 this does no array allocations:
```
require "memory_profiler"

class Foo
  def bar
    Bar.new
  end
end

class Bar
  def baz
  end
end

foo = Foo.new
report = MemoryProfiler.report do
  [1,2].include?(foo.bar.baz)
end.pretty_print
```

Also, this code is simply slower on Ruby 3.4:

```rb
require "benchmark/ips"

local = [301, 302]
val = 301
Benchmark.ips do |x|
  x.report('local') do
    local.include?(val)
  end

  x.report('literal') do
    [301, 302].include?(val)
  end
  x.compare!
end
```

```
$ ruby test.rb
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [x86_64-linux]
Warming up --------------------------------------
               local     1.822M i/100ms
             literal     2.296M i/100ms
Calculating -------------------------------------
               local     18.235M (± 1.6%) i/s   (54.84 ns/i) -     92.906M in   5.096277s
             literal     22.807M (± 1.5%) i/s   (43.85 ns/i) -    114.789M in   5.034289s

Comparison:
             literal: 22806932.0 i/s
               local: 18235340.7 i/s - 1.25x  slower
```
Earlopain added a commit to Earlopain/rubocop-performance that referenced this issue Jan 30, 2025
… register offenses for `Array#include?` that are optimized directly in Ruby.

Since `include?` on arrays are the only instances I ever run across this cop, I think it makes sense to add special handling for this.

On Ruby 3.4 this does no array allocations:
```
require "memory_profiler"

class Foo
  def bar
    Bar.new
  end
end

class Bar
  def baz
  end
end

foo = Foo.new
report = MemoryProfiler.report do
  [1,2].include?(foo.bar.baz)
end.pretty_print
```

Also, this code is simply slower on Ruby 3.4:

```rb
require "benchmark/ips"

local = [301, 302]
val = 301
Benchmark.ips do |x|
  x.report('local') do
    local.include?(val)
  end

  x.report('literal') do
    [301, 302].include?(val)
  end
  x.compare!
end
```

```
$ ruby test.rb
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [x86_64-linux]
Warming up --------------------------------------
               local     1.822M i/100ms
             literal     2.296M i/100ms
Calculating -------------------------------------
               local     18.235M (± 1.6%) i/s   (54.84 ns/i) -     92.906M in   5.096277s
             literal     22.807M (± 1.5%) i/s   (43.85 ns/i) -    114.789M in   5.034289s

Comparison:
             literal: 22806932.0 i/s
               local: 18235340.7 i/s - 1.25x  slower
```
@koic koic closed this as completed in #488 Feb 27, 2025
@koic koic closed this as completed in d196b53 Feb 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
1 participant