From: Eric Wong <e@80x24.org>
To: spew@80x24.org
Subject: [PATCH] pathname: reduce object allocation
Date: Wed, 16 Sep 2015 09:25:28 +0000 [thread overview]
Message-ID: <20150916-Feature-11375-tweak@v1> (raw)
From schneems:
* ext/pathname/lib/pathname.rb: freeze string literals for
reduced object allocation.
[Feature #11375]
[ew:
- tweak to avoid introducing long lines, keep at 80 columns
(I use gigantic fonts)
- avoid freezing for case dispatch, that is already optimized
- favor concatstrings insn ("#{path}a" instead of: path + 'a'.freeze)
since concatstrings already avoids duplicating literals
... Possible future optimizations I noticed:
* Avoid: while !foo.empty? && foo.first == '.'.freeze
Instead: while foo[0] == '.'.freeze
This avoids an extra method call and Array#[] has optimized
dispatch for a single numeric arg. An empty array will
return nil which does not match '.'
]
ref: https://bugs.ruby-lang.org/issues/11375
---
ext/pathname/lib/pathname.rb | 55 +++++++++++++++++++++++++-------------------
1 file changed, 31 insertions(+), 24 deletions(-)
diff --git a/ext/pathname/lib/pathname.rb b/ext/pathname/lib/pathname.rb
index 9f23ba5..b41ec99 100644
--- a/ext/pathname/lib/pathname.rb
+++ b/ext/pathname/lib/pathname.rb
@@ -61,7 +61,9 @@ class Pathname
File.dirname(prefix)
elsif /#{SEPARATOR_PAT}/o =~ prefix
prefix = File.dirname(prefix)
- prefix = File.join(prefix, "") if File.basename(prefix + 'a') != 'a'
+ if File.basename("#{prefix}a") != 'a'.freeze
+ prefix = File.join(prefix, "".freeze)
+ end
prefix + relpath
else
prefix + relpath
@@ -102,7 +104,7 @@ class Pathname
when '..'
names.unshift base
else
- if names[0] == '..'
+ if names[0] == '..'.freeze
names.shift
else
names.unshift base
@@ -111,7 +113,7 @@ class Pathname
end
pre.tr!(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
if /#{SEPARATOR_PAT}/o =~ File.basename(pre)
- names.shift while names[0] == '..'
+ names.shift while names[0] == '..'.freeze
end
self.class.new(prepend_prefix(pre, File.join(*names)))
end
@@ -130,10 +132,11 @@ class Pathname
# add_trailing_separator(path) -> path
def add_trailing_separator(path) # :nodoc:
- if File.basename(path + 'a') == 'a'
+ if File.basename("#{path}a") == 'a'.freeze
path
else
- File.join(path, "") # xxx: Is File.join is appropriate to add separator?
+ # xxx: Is File.join is appropriate to add separator?
+ File.join(path, "".freeze)
end
end
private :add_trailing_separator
@@ -156,16 +159,16 @@ class Pathname
pre = path
while r = chop_basename(pre)
pre, base = r
- names.unshift base if base != '.'
+ names.unshift base if base != '.'.freeze
end
pre.tr!(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
if /#{SEPARATOR_PAT}/o =~ File.basename(pre)
- names.shift while names[0] == '..'
+ names.shift while names[0] == '..'.freeze
end
if names.empty?
self.class.new(File.dirname(pre))
else
- if names.last != '..' && File.basename(path) == '.'
+ if names.last != '..'.freeze && File.basename(path) == '.'.freeze
names << '.'
end
result = prepend_prefix(pre, File.join(*names))
@@ -182,7 +185,7 @@ class Pathname
#
# This is same as <code>self + '..'</code>.
def parent
- self + '..'
+ self + '..'.freeze
end
# Returns +true+ if +self+ points to a mountpoint.
@@ -239,7 +242,7 @@ class Pathname
while r = chop_basename(path)
path, = r
end
- path == ''
+ path == ''.freeze
end
#
@@ -359,17 +362,19 @@ class Pathname
index_list2.unshift prefix2.length
basename_list2.unshift basename2
end
- return path2 if prefix2 != ''
+ return path2 if prefix2 != ''.freeze
prefix1 = path1
while true
- while !basename_list2.empty? && basename_list2.first == '.'
+ while !basename_list2.empty? && basename_list2.first == '.'.freeze
index_list2.shift
basename_list2.shift
end
break unless r1 = chop_basename(prefix1)
prefix1, basename1 = r1
- next if basename1 == '.'
- if basename1 == '..' || basename_list2.empty? || basename_list2.first != '..'
+ next if basename1 == '.'.freeze
+ if basename1 == '..'.freeze ||
+ basename_list2.empty? ||
+ basename_list2.first != '..'.freeze
prefix1 = prefix1 + basename1
break
end
@@ -378,7 +383,7 @@ class Pathname
end
r1 = chop_basename(prefix1)
if !r1 && /#{SEPARATOR_PAT}/o =~ File.basename(prefix1)
- while !basename_list2.empty? && basename_list2.first == '..'
+ while !basename_list2.empty? && basename_list2.first == '..'.freeze
index_list2.shift
basename_list2.shift
end
@@ -436,10 +441,10 @@ class Pathname
# the directory because they are not children.
#
def children(with_directory=true)
- with_directory = false if @path == '.'
+ with_directory = false if @path == '.'.freeze
result = []
Dir.foreach(@path) {|e|
- next if e == '.' || e == '..'
+ next if e == '.'.freeze || e == '..'.freeze
if with_directory
result << self.class.new(File.join(@path, e))
else
@@ -507,13 +512,13 @@ class Pathname
dest_names = []
while r = chop_basename(dest_prefix)
dest_prefix, basename = r
- dest_names.unshift basename if basename != '.'
+ dest_names.unshift basename if basename != '.'.freeze
end
base_prefix = base_directory
base_names = []
while r = chop_basename(base_prefix)
base_prefix, basename = r
- base_names.unshift basename if basename != '.'
+ base_names.unshift basename if basename != '.'.freeze
end
unless SAME_PATHS[dest_prefix, base_prefix]
raise ArgumentError, "different prefix: #{dest_prefix.inspect} and #{base_directory.inspect}"
@@ -524,13 +529,13 @@ class Pathname
dest_names.shift
base_names.shift
end
- if base_names.include? '..'
+ if base_names.include? '..'.freeze
raise ArgumentError, "base_directory has ..: #{base_directory.inspect}"
end
- base_names.fill('..')
+ base_names.fill('..'.freeze)
relpath_names = base_names + dest_names
if relpath_names.empty?
- Pathname.new('.')
+ Pathname.new('.'.freeze)
else
Pathname.new(File.join(*relpath_names))
end
@@ -556,8 +561,10 @@ class Pathname # * Find *
def find(ignore_error: true) # :yield: pathname
return to_enum(__method__, ignore_error: ignore_error) unless block_given?
require 'find'
- if @path == '.'
- Find.find(@path, ignore_error: ignore_error) {|f| yield self.class.new(f.sub(%r{\A\./}, '')) }
+ if @path == '.'.freeze
+ Find.find(@path, ignore_error: ignore_error) do |f|
+ yield self.class.new(f.sub(%r{\A\./}, ''.freeze))
+ end
else
Find.find(@path, ignore_error: ignore_error) {|f| yield self.class.new(f) }
end
--
EW
next reply other threads:[~2015-09-16 9:25 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-09-16 9:25 Eric Wong [this message]
2015-09-16 9:26 ` [PATCH] pathname: reduce object allocation Eric Wong
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20150916-Feature-11375-tweak@v1 \
--to=e@80x24.org \
--cc=spew@80x24.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).