Actions
Bug #22131
openAvoid array allocation in `Array#include?` on literal arrays
Bug #22131:
Avoid array allocation in `Array#include?` on literal arrays
Description
I've noticed that Array#include? on a literal array of frozen-shareable elements (Integers, Symbols, true/false/nil, frozen String literals) compiles the receiver to duparray, which allocates a fresh Array copy on every call — even though #include? only reads it.
Before
# frozen_string_literal: true
[1, 2].include?(3)
== disasm: #<ISeq:<main>@-e:2 (2,0)-(2,18)>
0000 duparray [1, 2] ( 2)[Li]
0002 putobject 3
0004 opt_send_without_block <calldata!mid:include?, argc:1, ARGS_SIMPLE>
0006 leave
After
# frozen_string_literal: true
[1, 2].include?(3)
== disasm: #<ISeq:<main>@-e:2 (2,0)-(2,18)>
0000 putobject 3 ( 2)[Li]
0002 opt_duparray_send [1, 2], :include?, 1
0006 leave
Benchmarks
require 'benchmark/ips'
GC.disable
ARR = [1, 2, 3]
Benchmark.ips do |x|
x.report "literal [1,2,3].include?(2)" do |loop|
loop.times { [1, 2, 3].include?(2) }
end
x.report "constant ARR.include?(2)" do |loop|
loop.times { ARR.include?(2) }
end
x.compare!
end
| case | master | candidate | speedup |
|---|---|---|---|
[1,2,3].include?(2) |
17.83M i/s (56.09 ns) | 30.00M i/s (33.33 ns) | 1.68x |
ARR.include?(2) (const) |
23.92M i/s (41.80 ns) | 23.92M i/s (41.81 ns) | ~1.00x |
Github PR: https://github.com/ruby/ruby/pull/17496
Updated by nobu (Nobuyoshi Nakada) about 5 hours ago
Also putnil could be optimized.
Updated by Strech (Sergey Fedorov) about 5 hours ago
· Edited
nobu (Nobuyoshi Nakada) wrote in #note-1:
Also
putnilcould be optimized.
Yes, I thought about it, but decide to start small, I'm going to add it
UPD: Added in 2c368383572
Updated by Strech (Sergey Fedorov) about 3 hours ago
nobu (Nobuyoshi Nakada) wrote in #note-1:
Also
putnilcould be optimized.
PR is adjusted and green. Thanks for the suggestion
Actions