Skip to content

Instantly share code, notes, and snippets.

@JonathonMA
Last active Dec 31, 2015
Embed
What would you like to do?

Zippo/ruby performance comparison

These scripts compare the performance of my zip library (Zippo) vs. the ruby standard (rubyzip).

Example run

$ ruby --version

ruby 2.0.0p353 (2013-11-22 revision 43784) [x86_64-linux]

$ rake --quiet

Comparing list_zip...
Running each test once. Test will take about 5 seconds.
list_zip (zippo) is faster than list_zip (rubyzip) by 15x ± 0.1

Comparing expand_zip...
Running each test once. Test will take about 23 seconds.
expand_zip (zippo) is faster than expand_zip (rubyzip) by 4x ± 0.1

Single small file
Comparing write_zip...
Running each test once. Test will take about 1 second.
write_zip (zippo) is faster than write_zip (rubyzip) by 2x ± 0.1

Single large file
Comparing write_zip...
Running each test once. Test will take about 5 seconds.
write_zip (zippo) is faster than write_zip (rubyzip) by 10.000000000000009% ± 1.0%

Small files
Comparing write_zip...
Running each test once. Test will take about 2 seconds.
write_zip (zippo) is similar to write_zip (rubyzip)

Large files
Comparing write_zip...
Running each test once. Test will take about 7 seconds.
write_zip (zippo) is faster than write_zip (rubyzip) by 10.000000000000009% ± 1.0%
require 'fruity'
$impls = {}
def register sym, klass
$impls[sym] = klass
end
require_relative 'rubyzip'
require_relative 'zippo'
require 'tmpdir'
def in_tmp
Dir.mktmpdir do |dir|
Dir.chdir(dir) {
yield
}
end
end
def expand_paths paths
paths.map { |fn| File.expand_path fn }
end
method = ARGV.shift.to_sym
args = ARGV
runs = $impls.each_with_object(
Hash.new { |h,k| h[k] = {} }
) do |ipair, hash|
sym, klass = *ipair
m_name = "#{method} (#{sym})"
m_obj = Object.new
m_obj.singleton_class.send(:include, klass)
hash[m_name] = -> {
in_tmp { m_obj.send method, *args }
}
end
puts "Comparing #{method}..."
compare runs
puts
source 'https://rubygems.org'
gem 'fruity', '~> 0.2.0'
gem 'rubyzip', '1.1.0'
gem 'zippo', '0.2.0'
DEFAULT_ZIP = "zoneinfo.zip"
LARGE_FILE = "large.file"
SMALL_FILE = "small.file"
file SMALL_FILE do
srand 0xdeadbeef
words = IO.read("/usr/share/dict/words").lines.to_a
data = ""
begin
data << words[rand(words.size)]
end until data.size > 10240
File.open(SMALL_FILE, "w") {|f| f << data }
end
file LARGE_FILE => SMALL_FILE do
tmp = IO.read(SMALL_FILE)
buf = ""
begin
buf << tmp
end until buf.size > 10_000_000
File.open(LARGE_FILE, "w") {|f| f << buf }
end
require 'fileutils'
file "small_files/x0159" => SMALL_FILE do |t|
FileUtils.mkdir_p "small_files"
Dir.chdir("small_files") do
sh *%w[ split -a4 -d -b 64 ../small.file ]
end
end
file "large_files/x0090" => LARGE_FILE do |t|
FileUtils.mkdir_p "large_files"
Dir.chdir("large_files") do
sh *%w[ split -a4 -d -b 100K ../large.file ]
end
end
file DEFAULT_ZIP do |t|
target = File.expand_path t.name
Dir.chdir("/usr/share/zoneinfo") do
sh *%W[zip -qr #{target} .]
end
end
task :clean do
files = [DEFAULT_ZIP, LARGE_FILE, SMALL_FILE, "small_files", "large_files"]
files.each do |file|
rm_rf file if File.exist? file
end
end
def compare method, *args
ruby *(%W[compare.rb #{method}] + args)
end
def compare_write filespec
compare :write_zip, "test.zip", *expand_paths(Dir[filespec])
end
task :list_zip => DEFAULT_ZIP do |t|
compare t.name, *expand_paths(t.prerequisites)
end
task :expand_zip => DEFAULT_ZIP do |t|
compare t.name, *expand_paths(t.prerequisites)
end
def expand_paths paths
paths.map { |fn| File.expand_path fn }
end
task :write_small => SMALL_FILE do |t|
puts "Single small file"
compare_write *t.prerequisites
end
task :write_large => LARGE_FILE do |t|
puts "Single large file"
compare_write *t.prerequisites
end
task :write_small_files => "small_files/x0159" do
puts "Small files"
compare_write "small_files/x*"
end
task :write_large_files => "large_files/x0090" do
puts "Large files"
compare_write "large_files/x*"
end
task :default => [:list_zip, :expand_zip, :write_small, :write_large, :write_small_files, :write_large_files] do
end
module Rubyzip
require 'zip'
def list_zip(filename)
[].tap { |arr|
Zip::File.foreach(filename) do |file|
arr << file.name
end
}
end
def expand_zip(filename)
Zip::File.foreach(filename) do |file|
if file.directory?
Dir.mkdir(file.name)
else
file.get_input_stream { |io|
IO.write file.name, io.read
}
end
end
""
end
def write_zip(filename, *files)
Zip::File.open(filename, Zip::File::CREATE) do |zipfile|
files.each do |file|
zipfile.add File.basename(file), file
end
end
end
end
register :rubyzip, Rubyzip
module Zippo
require 'zippo'
def list_zip(filename)
Zippo::ZipFile.open(filename) do |v|
v.map(&:name)
end
end
def expand_zip(filename)
Zippo.open(filename) do |v|
v.directory.each do |entry|
if entry.directory?
Dir.mkdir(entry.name)
else
IO.write entry.name, entry.read
end
end
end
""
end
def write_zip(filename, *file_list)
Zippo.open(filename, 'w') do |v|
file_list.each do |file|
v.directory.insert File.basename(file), File.open(file)
end
end
end
end
register :zippo, Zippo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment