[rubygems/rubygems] Fewer allocations in gem installation
For now, on a small rails app I have hanging around: ``` ==> memprof.after.txt <== Total allocated: 872.51 MB (465330 objects) Total retained: 40.48 kB (326 objects) ==> memprof.before.txt <== Total allocated: 890.79 MB (1494026 objects) Total retained: 40.40 kB (328 objects) ``` Not a huge difference in memory usage, but it's a drastic improvement in total number of allocations. Additionally, this will pay huge dividends once https://github.com/ruby/zlib/pull/61 is merged, as it will allow us to completely avoid allocations in the repeated calls to readpartial, which currently accounts for most of the memory usage shown above. https://github.com/rubygems/rubygems/commit/f78d45d927
This commit is contained in:
parent
4a94ce8569
commit
505715ddf1
@ -224,11 +224,11 @@ class Gem::Installer
|
|||||||
|
|
||||||
File.open generated_bin, "rb" do |io|
|
File.open generated_bin, "rb" do |io|
|
||||||
line = io.gets
|
line = io.gets
|
||||||
shebang = /^#!.*ruby/
|
shebang = /^#!.*ruby/o
|
||||||
|
|
||||||
# TruffleRuby uses a bash prelude in default launchers
|
# TruffleRuby uses a bash prelude in default launchers
|
||||||
if load_relative_enabled? || RUBY_ENGINE == "truffleruby"
|
if load_relative_enabled? || RUBY_ENGINE == "truffleruby"
|
||||||
until line.nil? || line =~ shebang do
|
until line.nil? || shebang.match?(line) do
|
||||||
line = io.gets
|
line = io.gets
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -239,7 +239,7 @@ class Gem::Installer
|
|||||||
|
|
||||||
# TODO: detect a specially formatted comment instead of trying
|
# TODO: detect a specially formatted comment instead of trying
|
||||||
# to find a string inside Ruby code.
|
# to find a string inside Ruby code.
|
||||||
next unless io.gets.to_s.include?("This file was generated by RubyGems")
|
next unless io.gets&.include?("This file was generated by RubyGems")
|
||||||
|
|
||||||
ruby_executable = true
|
ruby_executable = true
|
||||||
existing = io.read.slice(/
|
existing = io.read.slice(/
|
||||||
|
@ -357,18 +357,21 @@ EOM
|
|||||||
|
|
||||||
def digest(entry) # :nodoc:
|
def digest(entry) # :nodoc:
|
||||||
algorithms = if @checksums
|
algorithms = if @checksums
|
||||||
@checksums.keys
|
@checksums.to_h {|algorithm, _| [algorithm, Gem::Security.create_digest(algorithm)] }
|
||||||
else
|
elsif Gem::Security::DIGEST_NAME
|
||||||
[Gem::Security::DIGEST_NAME].compact
|
{ Gem::Security::DIGEST_NAME => Gem::Security.create_digest(Gem::Security::DIGEST_NAME) }
|
||||||
end
|
end
|
||||||
|
|
||||||
algorithms.each do |algorithm|
|
return @digests if algorithms.nil? || algorithms.empty?
|
||||||
digester = Gem::Security.create_digest(algorithm)
|
|
||||||
|
|
||||||
digester << entry.readpartial(16_384) until entry.eof?
|
|
||||||
|
|
||||||
|
buf = String.new(capacity: 16_384, encoding: Encoding::BINARY)
|
||||||
|
until entry.eof?
|
||||||
|
entry.readpartial(16_384, buf)
|
||||||
|
algorithms.each_value {|digester| digester << buf }
|
||||||
|
end
|
||||||
entry.rewind
|
entry.rewind
|
||||||
|
|
||||||
|
algorithms.each do |algorithm, digester|
|
||||||
@digests[algorithm][entry.full_name] = digester
|
@digests[algorithm][entry.full_name] = digester
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -437,8 +440,6 @@ EOM
|
|||||||
|
|
||||||
FileUtils.rm_rf destination
|
FileUtils.rm_rf destination
|
||||||
|
|
||||||
mkdir_options = {}
|
|
||||||
mkdir_options[:mode] = dir_mode ? 0o755 : (entry.header.mode if entry.directory?)
|
|
||||||
mkdir =
|
mkdir =
|
||||||
if entry.directory?
|
if entry.directory?
|
||||||
destination
|
destination
|
||||||
@ -447,7 +448,7 @@ EOM
|
|||||||
end
|
end
|
||||||
|
|
||||||
unless directories.include?(mkdir)
|
unless directories.include?(mkdir)
|
||||||
FileUtils.mkdir_p mkdir, **mkdir_options
|
FileUtils.mkdir_p mkdir, mode: dir_mode ? 0o755 : (entry.header.mode if entry.directory?)
|
||||||
directories << mkdir
|
directories << mkdir
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -707,6 +708,7 @@ EOM
|
|||||||
|
|
||||||
def verify_gz(entry) # :nodoc:
|
def verify_gz(entry) # :nodoc:
|
||||||
Zlib::GzipReader.wrap entry do |gzio|
|
Zlib::GzipReader.wrap entry do |gzio|
|
||||||
|
# TODO: read into a buffer once zlib supports it
|
||||||
gzio.read 16_384 until gzio.eof? # gzip checksum verification
|
gzio.read 16_384 until gzio.eof? # gzip checksum verification
|
||||||
end
|
end
|
||||||
rescue Zlib::GzipFile::Error => e
|
rescue Zlib::GzipFile::Error => e
|
||||||
|
@ -127,7 +127,8 @@ class Gem::Package::TarHeader
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.strict_oct(str)
|
def self.strict_oct(str)
|
||||||
return str.strip.oct if /\A[0-7]*\z/.match?(str.strip)
|
str.strip!
|
||||||
|
return str.oct if /\A[0-7]*\z/.match?(str)
|
||||||
|
|
||||||
raise ArgumentError, "#{str.inspect} is not an octal string"
|
raise ArgumentError, "#{str.inspect} is not an octal string"
|
||||||
end
|
end
|
||||||
@ -137,7 +138,8 @@ class Gem::Package::TarHeader
|
|||||||
# \ff flags a negative 256-based number
|
# \ff flags a negative 256-based number
|
||||||
# In case we have a match, parse it as a signed binary value
|
# In case we have a match, parse it as a signed binary value
|
||||||
# in big-endian order, except that the high-order bit is ignored.
|
# in big-endian order, except that the high-order bit is ignored.
|
||||||
return str.unpack("N2").last if /\A[\x80\xff]/n.match?(str)
|
|
||||||
|
return str.unpack1("@4N") if /\A[\x80\xff]/n.match?(str)
|
||||||
strict_oct(str)
|
strict_oct(str)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -149,21 +151,23 @@ class Gem::Package::TarHeader
|
|||||||
raise ArgumentError, ":name, :size, :prefix and :mode required"
|
raise ArgumentError, ":name, :size, :prefix and :mode required"
|
||||||
end
|
end
|
||||||
|
|
||||||
vals[:uid] ||= 0
|
@checksum = vals[:checksum] || ""
|
||||||
vals[:gid] ||= 0
|
@devmajor = vals[:devmajor] || 0
|
||||||
vals[:mtime] ||= 0
|
@devminor = vals[:devminor] || 0
|
||||||
vals[:checksum] ||= ""
|
@gid = vals[:gid] || 0
|
||||||
vals[:typeflag] = "0" if vals[:typeflag].nil? || vals[:typeflag].empty?
|
@gname = vals[:gname] || "wheel"
|
||||||
vals[:magic] ||= "ustar"
|
@linkname = vals[:linkname]
|
||||||
vals[:version] ||= "00"
|
@magic = vals[:magic] || "ustar"
|
||||||
vals[:uname] ||= "wheel"
|
@mode = vals[:mode]
|
||||||
vals[:gname] ||= "wheel"
|
@mtime = vals[:mtime] || 0
|
||||||
vals[:devmajor] ||= 0
|
@name = vals[:name]
|
||||||
vals[:devminor] ||= 0
|
@prefix = vals[:prefix]
|
||||||
|
@size = vals[:size]
|
||||||
FIELDS.each do |name|
|
@typeflag = vals[:typeflag]
|
||||||
instance_variable_set "@#{name}", vals[name]
|
@typeflag = "0" if @typeflag.nil? || @typeflag.empty?
|
||||||
end
|
@uid = vals[:uid] || 0
|
||||||
|
@uname = vals[:uname] || "wheel"
|
||||||
|
@version = vals[:version] || "00"
|
||||||
|
|
||||||
@empty = vals[:empty]
|
@empty = vals[:empty]
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user