From 763e3dc0d1d4cecded327e77f7c8f49d0a383c13 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Mon, 27 Jan 2020 12:20:12 +0100 Subject: [PATCH] Micro optimization: `[0...-3] = ''` is faster than `slice!(-3, 3)` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 ``` --- lib/zeitwerk/loader.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zeitwerk/loader.rb b/lib/zeitwerk/loader.rb index c7c705bb..d4182b0f 100644 --- a/lib/zeitwerk/loader.rb +++ b/lib/zeitwerk/loader.rb @@ -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)