Rails.root.join speed vs regular string

September 12, 2019 Link to post  Permalink

A question came up today because of a Rubocop rule.  We have enabled the Rails/FilePath rule by default, and this warns about code that builds file paths in a string, and suggests you use Rails.root.join("dir1", "dir2", "file") instead.

This rule can be useful for code that is likely to be used on many different platforms, as it ensures that the correct path separator for the current platform is used to build the path.  However, many apps will never be used on significantly different platforms, so this rule has a cost without a benefit for many users.

Paraphrasing many tweets to @tenderlove - But at what cost?

Benchmarking Pathname.join and String Interpolation

The Rails.root object is an instance of Ruby's built-in Pathname class.  So I'll be testing this directly.  String Interpolation is built into Ruby and looks like "#{foo}".  

Here's the two lines of code I'm testing:

var = root.join("tmp", "path", "file").to_s

and 

var = "#{root}/tmp/path/file"

with a single setup for the root variable

root = Pathname.new("/home/foo/bar/app")

Here's the benchmark gist

Results

Warming up --------------------------------------
       pathname join     5.643k i/100ms
       string iterp.   252.100k i/100ms
Calculating -------------------------------------
       pathname join     57.791k (± 4.4%) i/s -    293.436k in   5.086740s
       string iterp.      3.994M (± 2.1%) i/s -     20.168M in   5.051901s

Comparison:
       string iterp.:  3993877.9 i/s
       pathname join:    57791.3 i/s - 69.11x  slower

Oh dear.  The String Interpolation of this code is about 70 times faster than the Pathname.join version.  It turns out the cost of this is quite high!

Now, if this code is not in a performance sensitive part of your code, the differences are not likely to matter - 60,000 times per second for the slow version still isn't very slow.  But if you're building paths in tight loops, or in code that needs to respond with low latency, then you will want to look at using the string interpolation version and disabling the Rubocop check!