Skip to content

Instantly share code, notes, and snippets.

@heycarsten
Created January 14, 2009 06:38
Show Gist options
  • Save heycarsten/46819 to your computer and use it in GitHub Desktop.
Save heycarsten/46819 to your computer and use it in GitHub Desktop.
# Self-If-Self: A Study
#
# I have decided that this is silly, but I want to share it to see what other
# people think.
#
require 'benchmark'
require 'pp'
class Object
def if
yield(self) if self
end
end
update_with_flat_person = {
:person_name => 'Carsten',
:person_id => 23,
:text => 'Known to be quirky at times.' }
update_with_actual_person = {
:person => {
:name => 'Carsten',
:id => 23 },
:text => 'Known to be quirky at times.' }
# This would be normal
def build(attributes)
person = attributes.delete(:person)
if person
attributes[:person_id] = person[:id]
attributes[:person_name] = person[:name]
end
attributes
end
pp build(update_with_actual_person)
# And this would be one of my crack-pot delusions...
def self_if_self_build(attributes)
attributes.delete(:person).if do |person|
attributes[:person_id] = person[:id]
attributes[:person_name] = person[:name]
end
attributes
end
pp self_if_self_build(update_with_actual_person)
# The normal approach is almost two times faster than the self-if-self
# monkeypatch magic on a 2.2 GHz MacBook Pro with 4 GB of RAM.
#
# The actual numbers on my machine:
# user system total real
# Normal Approach 4.540000 1.940000 6.480000 ( 6.497931)
# Self-If-Self Monkeypatch 8.300000 3.870000 12.170000 ( 12.207854)
#
Benchmark.bmbm do |b|
b.report('Normal Approach') do
500_000.times { build update_with_flat_person }
500_000.times { build update_with_actual_person }
end
b.report('Self-If-Self Monkeypatch') do
500_000.times { self_if_self_build update_with_flat_person }
500_000.times { self_if_self_build update_with_actual_person }
end
end
# Perhaps a better approach (at least in this case)
def inject_flatten(hsh, target, target_keys)
hsh.keys.inject({}) do |output, key|
if target == key
target_keys.each { |k| output[:"#{target}_#{k}"] = hsh[target][k] }
else
output[key] = hsh[key]
end
output
end
end
def inject_flatten_build(attributes)
inject_flatten attributes, :person, [:id, :name]
end
pp inject_flatten_build(update_with_actual_person)
# This one is curious as well, sure looks easier to read
def dup_flatten(hsh, target_key, target_keys)
result = hsh.dup
target = result.delete(target_key)
target_keys.each { |k| result[:"#{target_key}_#{k}"] = target[k] } if target
result
end
def dup_flatten_build(attributes)
dup_flatten attributes, :person, [:id, :name]
end
pp dup_flatten_build(update_with_actual_person)
# The inject_flatten approach seems pretty inefficient (duh.)
#
# user system total real
# Inject Flatten 36.560000 14.600000 51.160000 ( 51.558791)
# Copy Flatten 11.840000 3.900000 15.740000 ( 15.830560)
#
Benchmark.bmbm do |b|
b.report('Inject Flatten') do
500_000.times { inject_flatten_build update_with_flat_person }
500_000.times { inject_flatten_build update_with_actual_person }
end
b.report('Copy Flatten') do
500_000.times { dup_flatten_build update_with_flat_person }
500_000.times { dup_flatten_build update_with_actual_person }
end
end
# What if we do it in-place?
def in_place_flatten(hsh, target_key, target_keys)
target = hsh.delete(target_key)
target_keys.each { |k| hsh[:"#{target_key}_#{k}"] = target[k] } if target
hsh
end
def in_place_flatten_build(attributes)
in_place_flatten attributes, :person, [:id, :name]
end
pp in_place_flatten_build(update_with_actual_person)
# A lot faster, but modifying arguments can really bite you.
#
# user system total real
# In-Place Flatten 7.260000 2.940000 10.200000 ( 10.212538)
#
Benchmark.bmbm do |b|
b.report('In-Place Flatten') do
500_000.times { in_place_flatten_build update_with_flat_person }
500_000.times { in_place_flatten_build update_with_actual_person }
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment