# frozen_string_literal: true
require "benchmark/ips"
RECURSIVE_HASH_DUP = {
Integer => -> (val) { val },
Float => -> (val) { val },
TrueClass => -> (val) { val },
FalseClass => -> (val) { val },
NilClass => -> (val) { val },
NilClass => -> (val) { val },
String => ->(val) { val.dup },
Array => ->(val) {
duped = Array.new(val.size)
duped.each_with_index do |value, index|
duped[index] = RECURSIVE_HASH_DUP[value.class].call(value)
end
},
Hash => ->(val) {
duped = val.dup
duped.each_pair do |key, value|
duped[key] = RECURSIVE_HASH_DUP[value.class].call(value)
end
}
}
RECURSIVE_HASH_DUP.default_proc = ->(_, val) { |_, hash|
case val
when Array
RECURSIVE_HASH_DUP[Array].call(val)
when Hash
RECURSIVE_HASH_DUP[Hash].call(val)
else
val.dup
end
}
RECURSIVE_HASH_DUP.compare_by_identity.rehash
def recursive_hash_dup(obj)
RECURSIVE_HASH_DUP[obj.class].call(obj)
end
def deep_dup(obj)
case obj
when Integer, Float, TrueClass, FalseClass, NilClass
return obj
when String
return obj.dup
when Array
duped = Array.new(obj.size)
duped.each_with_index do |value, index|
duped[index] = deep_dup(value)
end
when Hash
duped = obj.dup
duped.each_pair do |key, value|
duped[key] = deep_dup(value)
end
else
duped = obj.dup
end
duped
end
def deep_dup_without_case(obj)
if Integer === obj || Float === obj || TrueClass === obj || FalseClass === obj || NilClass === obj
return obj
elsif String === obj
return obj.dup
elsif Array === obj
duped = Array.new(obj.size)
obj.each_with_index do |value, index|
duped[index] = deep_dup_without_case(value)
end
elsif Hash === obj
duped = obj.dup
duped.each_pair do |key, value|
duped[key] = deep_dup_without_case(value)
end
else
duped = obj.dup
end
duped
end
obj = { "class" => "FooWorker", "args" => [1, 2, 3, "foobar"], "jid" => "123987123" }
Benchmark.ips do |x|
x.report("marshal") do
Marshal.load(Marshal.dump(obj))
end
x.report("deep_dup") do
deep_dup(obj)
end
x.report("deep_dup_without_case") do
deep_dup_without_case(obj)
end
x.report("hash deep dup") do
recursive_hash_dup(obj)
end
x.compare!
end
$ ruby scratch.rb
Warming up --------------------------------------
marshal 14.343k i/100ms
deep_dup 27.024k i/100ms
deep_dup_without_case
31.578k i/100ms
hash deep dup 38.321k i/100ms
Calculating -------------------------------------
marshal 148.202k (± 3.5%) i/s - 745.836k in 5.039228s
deep_dup 285.230k (± 2.4%) i/s - 1.432M in 5.024573s
deep_dup_without_case
338.379k (± 5.1%) i/s - 1.705M in 5.056639s
hash deep dup 396.849k (± 8.9%) i/s - 1.993M in 5.066124s
Comparison:
hash deep dup: 396849.2 i/s
deep_dup_without_case: 338379.3 i/s - 1.17x slower
deep_dup: 285229.5 i/s - 1.39x slower
marshal: 148201.7 i/s - 2.68x slower