[ruby-core:105090] [Ruby master Feature#17287] Faster Pathname FileUtils methods
From:
"hsbt (Hiroshi SHIBATA)" <noreply@...>
Date:
2021-08-30 06:53:32 UTC
List:
ruby-core #105090
Issue #17287 has been updated by hsbt (Hiroshi SHIBATA).
Assignee set to hsbt (Hiroshi SHIBATA)
Status changed from Open to Closed
Merged at https://github.com/ruby/ruby/commit/51070ee5c4e83a4faa0feb72f08d1=
d9fef18b016
----------------------------------------
Feature #17287: Faster Pathname FileUtils methods
https://bugs.ruby-lang.org/issues/17287#change-93499
* Author: schneems (Richard Schneeman)
* Status: Closed
* Priority: Normal
* Assignee: hsbt (Hiroshi SHIBATA)
----------------------------------------
I have a patch that I would like to merge into Pathname for increased perfo=
rmance. I understand that akr maintains pathname and may not be on GitHub. =
Here is a link to my patch:
https://github.com/ruby/ruby/pull/3693
Here is the diff:
```
$ git diff master
diff --git a/ext/pathname/lib/pathname.rb b/ext/pathname/lib/pathname.rb
index e6fb90277d..c3af24837f 100644
--- a/ext/pathname/lib/pathname.rb
+++ b/ext/pathname/lib/pathname.rb
@@ -575,12 +575,13 @@ def find(ignore_error: true) # :yield: pathname
class Pathname # * FileUtils *
+ autoload(:FileUtils, 'fileutils')
+
# Creates a full path, including any intermediate directories that don't=
yet
# exist.
#
# See FileUtils.mkpath and FileUtils.mkdir_p
def mkpath
- require 'fileutils'
FileUtils.mkpath(@path)
nil
end
@@ -591,7 +592,6 @@ def mkpath
def rmtree
# The name "rmtree" is borrowed from File::Path of Perl.
# File::Path provides "mkpath" and "rmtree".
- require 'fileutils'
FileUtils.rm_r(@path)
nil
end
```
## Description
Currently when calling any of the "FileUtils" methods on pathname `require`=
is called every time even though that library might already be loaded. Thi=
s is slow.
We can speed it up by either checking first if the constant is already defi=
ned, or by using autoload.
Using defined speeds up the action by about 300x and using autoload is abou=
t twice as fast as that (600x faster than current require method).
I'm proposing we use autoload:
```ruby
require 'benchmark/ips'
Benchmark.ips do |x|
autoload(:FileUtils, "fileutils")
x.report("require") { require 'fileutils' }
x.report("defined") { require 'fileutils' unless defined?(FileUtils) }
x.report("autoload") { FileUtils }
x.compare!
end
# Warming up --------------------------------------
# require 3.624k i/100ms
# defined 1.465M i/100ms
# autoload 2.320M i/100ms
# Calculating -------------------------------------
# require 36.282k (=B1 2.4%) i/s - 184.824k in 5.0971=
53s
# defined 14.539M (=B1 2.0%) i/s - 73.260M in 5.0411=
61s
# autoload 23.100M (=B1 1.9%) i/s - 115.993M in 5.0232=
71s
# Comparison:
# autoload: 23099779.2 i/s
# defined: 14538544.9 i/s - 1.59x (=B1 0.00) slower
# require: 36282.3 i/s - 636.67x (=B1 0.00) slower
```
Because this autoload is scoped to Pathname it will not change the behavior=
of existing programs that are not expecting FileUtils to be loaded yet:
```
ruby -rpathname -e "class Pathname; autoload(:FileUtils, 'fileutils'); end;=
puts FileUtils.exist?('foo')"
Traceback (most recent call last):
-e:1:in `<main>': uninitialized constant FileUtils (NameError)
```
-- =
https://bugs.ruby-lang.org/
Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=3Dunsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>