Use an empty string when building File.expand_path

Allocating a string of length MAXPATHLEN and then shrinking the string
is inefficient when the resulting path is short. Preallocating a large
string is also a problem for Variable Width Allocation since we can't
easily downsize the capacity.

I ran the following benchmark:

```ruby
Benchmark.ips do |x|
  {
    "empty" => "",
    "short" => "a/" * 10,
    "medium" => "a/" * 100,
    "long" => "a/" * 500
  }.each do |name, path|
    x.report(name) do |times|
      i = 0
      while i < times
        File.expand_path(path)
        i += 1
      end
    end
  end
end
```

On this commit:

```
 empty     97.486k (± 0.7%) i/s -    492.915k in   5.056507s
 short     96.026k (± 2.4%) i/s -    486.489k in   5.068966s
medium     86.304k (± 1.3%) i/s -    435.336k in   5.045112s
  long     59.395k (± 1.7%) i/s -    302.175k in   5.089026s
```

On master:

```
 empty     94.138k (± 1.4%) i/s -    472.158k in   5.016590s
 short     92.043k (± 1.4%) i/s -    468.180k in   5.087496s
medium     84.910k (± 2.3%) i/s -    425.750k in   5.017007s
  long     61.503k (± 2.7%) i/s -    309.723k in   5.039429s
```
This commit is contained in:
Peter Zhu 2022-04-12 09:18:41 -04:00
parent d0a822eec5
commit 381475f02e
Notes: git 2022-04-12 22:55:27 +09:00
2 changed files with 3 additions and 2 deletions

2
file.c
View File

@ -4035,7 +4035,7 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
}
#endif /* _WIN32 */
#define EXPAND_PATH_BUFFER() rb_usascii_str_new(0, MAXPATHLEN + 2)
#define EXPAND_PATH_BUFFER() rb_usascii_str_new(0, 1)
static VALUE
str_shrink(VALUE str)

View File

@ -874,7 +874,8 @@ class TestFileExhaustive < Test::Unit::TestCase
path = File.expand_path("/foo")
assert_operator(ObjectSpace.memsize_of(path), :<=, path.bytesize + GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE], bug9934)
path = File.expand_path("/a"*25)
assert_equal(path.bytesize + 1 + GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE], ObjectSpace.memsize_of(path), bug9934)
assert_operator(ObjectSpace.memsize_of(path), :<=,
(path.bytesize + 1) * 2 + GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE], bug9934)
end
def test_expand_path_encoding