Files
ruby/benchmark/array_flatten.yml
Jean Boussier 37325e9f7a Array#flatten: optimize the common *args.flatten pattern
A common Ruby idiom for variadic methods is to flatten the argument
list so that it can be called with either variadic arguments or an
Array.

e.g. from the sqlite3 gem:

```ruby
def bind_params(*bind_vars)
  bind_vars.flatten.each do |var|
  # ...
```

However, as soon as `flatten` encounter another array, it has to
protect against recursion, which requires allocating an expensive
identity Hash.
So this pattern has a relatively high cost when called with
a single array argument (e.g. `bind_params [1, 2, 3]`).

We can specialize for that common case without noticeably
impacting other usages of `Array#flatten`:

|                              |compare-ruby|built-ruby|
|:-----------------------------|-----------:|---------:|
|small_flat_ary.flatten        |      7.547M|    7.974M|
|                              |           -|     1.06x|
|small_flat_ary.flatten!       |      6.031M|    6.105M|
|                              |           -|     1.01x|
|large_flat_ary.flatten        |    465.896k|  485.861k|
|                              |           -|     1.04x|
|large_flat_ary.flatten!       |    455.872k|  478.103k|
|                              |           -|     1.05x|
|small_pairs_ary.flatten       |      1.327M|    1.407M|
|                              |           -|     1.06x|
|small_pairs_ary.flatten!      |      1.165M|    1.153M|
|                              |       1.01x|         -|
|large_pairs_ary.flatten       |     96.612k|   96.976k|
|                              |           -|     1.00x|
|large_pairs_ary.flatten!      |     94.108k|   97.289k|
|                              |           -|     1.03x|
|mostly_flat_ary.flatten       |    399.648k|  417.327k|
|                              |           -|     1.04x|
|mostly_flat_ary.flatten!      |    378.315k|  395.946k|
|                              |           -|     1.05x|
|small_nested_ary.flatten      |      2.513M|    7.788M|
|                              |           -|     3.10x|
|small_nested_ary.flatten!     |      2.024M|    5.914M|
|                              |           -|     2.92x|
|large_nested_ary.flatten      |    341.635k|  485.319k|
|                              |           -|     1.42x|
|large_nested_ary.flatten!     |    329.837k|  475.014k|
|                              |           -|     1.44x|
|small_nested_ary.flatten(1)   |      9.497M|   61.350M|
|                              |           -|     6.46x|
|small_nested_ary.flatten!(1)  |      5.136M|   17.668M|
|                              |           -|     3.44x|
|large_nested_ary.flatten(1)   |      1.575M|   60.241M|
|                              |           -|    38.26x|
|large_nested_ary.flatten!(1)  |      1.369M|   16.835M|
|                              |           -|    12.30x|
2026-08-22 22:13:33 +02:00

30 lines
1.3 KiB
YAML

prelude: |
small_flat_ary = 5.times.to_a
large_flat_ary = 100.times.to_a
small_pairs_ary = [[1, 2]] * 5
large_pairs_ary = [[1, 2]] * 100
mostly_flat_ary = 100.times.to_a.push([101, 102])
small_nested_ary = [small_flat_ary]
large_nested_ary = [large_flat_ary]
benchmark:
small_flat_ary.flatten: small_flat_ary.flatten
small_flat_ary.flatten!: small_flat_ary.dup.flatten!
large_flat_ary.flatten: large_flat_ary.flatten
large_flat_ary.flatten!: large_flat_ary.dup.flatten!
small_pairs_ary.flatten: small_pairs_ary.flatten
small_pairs_ary.flatten!: small_pairs_ary.dup.flatten!
large_pairs_ary.flatten: large_pairs_ary.flatten
large_pairs_ary.flatten!: large_pairs_ary.dup.flatten!
mostly_flat_ary.flatten: mostly_flat_ary.flatten
mostly_flat_ary.flatten!: mostly_flat_ary.dup.flatten!
small_nested_ary.flatten: small_nested_ary.flatten
small_nested_ary.flatten!: small_nested_ary.dup.flatten!
large_nested_ary.flatten: large_nested_ary.flatten
large_nested_ary.flatten!: large_nested_ary.dup.flatten!
small_nested_ary.flatten(1): small_nested_ary.flatten(1)
small_nested_ary.flatten!(1): small_nested_ary.dup.flatten!(1)
large_nested_ary.flatten(1): large_nested_ary.flatten(1)
large_nested_ary.flatten!(1): large_nested_ary.dup.flatten!(1)
loop_count: 10000