Skip to content

Commit

Permalink
Micro optimization: [0...-3] = '' is faster than slice!(-3, 3)
Browse files Browse the repository at this point in the history
I noticed a surprising amount of allocations from this idiom:

```
Allocated String Report
-----------------------------------
    109381  ""
     13344  ~/gems/zeitwerk-2.2.2/lib/zeitwerk/loader.rb:479
```

Turns out `slice!` allocates a wierd empty string for no reasons,
as well as a string containing the sliced chars we don't care about:

```ruby
>> s = "foo.rb"; MemoryProfiler.report { s.slice!(-3, 3) }.pretty_print
Allocated String Report
-----------------------------------
         1  ""
         1  (irb):20

         1  ".rb"
         1  (irb):20
```

Based on the context, I assume the `slice!` usage goal was to save some
string allocations, but turns out it actually makes things worse.

Two other candidate methods could be:

  - `byteslice`, as it ignore encoding it's marginally faster than `slice`.
  - `delete_suffix!` saves an allocation, but was introduced in Ruby 2.5.
  - `[]= ''` since that's what `slice!` end up doing internally.

Benchmark:

```ruby
require 'benchmark/ips'

Benchmark.ips do |x|
  x.report('slice!(-3, 3)') { "foo.rb".slice!(-3, 3) }
  x.report('slice(0...-3)') { "foo.rb".slice(0...-3) }
  x.report('byteslice(0...-3)') { "foo.rb".byteslice(0...-3) }
  x.report('[-3..-1] = ""') { "foo.rb"[-3..-1] = ''.freeze }
  x.report('delete_suffix!') { "foo.rb".delete_suffix!(".rb".freeze) }
end
```

```
       slice!(-3, 3)      4.939M (± 1.0%) i/s -     24.900M in   5.042534s
       slice(0...-3)      7.323M (± 1.4%) i/s -     36.774M in   5.023076s
   byteslice(0...-3)      8.126M (± 1.5%) i/s -     40.930M in   5.038314s
       [-3..-1] = ""      7.842M (± 0.7%) i/s -     39.249M in   5.005565s
      delete_suffix!     10.657M (± 5.8%) i/s -     53.283M in   5.025248s
```
  • Loading branch information
byroot committed Jan 27, 2020
1 parent af53354 commit 763e3dc
Showing 1 changed file with 1 addition and 1 deletion.
2 changes: 1 addition & 1 deletion lib/zeitwerk/loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ def set_autoloads_in_dir(dir, parent)
ls(dir) do |basename, abspath|
begin
if ruby?(basename)
basename.slice!(-3, 3)
basename[-3..-1] = ''
cname = inflector.camelize(basename, abspath).to_sym
autoload_file(parent, cname, abspath)
elsif dir?(abspath)
Expand Down

0 comments on commit 763e3dc

Please sign in to comment.