Skip to content

Instantly share code, notes, and snippets.

@repeatedly
Created October 29, 2015 16:12
Show Gist options
  • Save repeatedly/bc6d692ef6192489d520 to your computer and use it in GitHub Desktop.
Save repeatedly/bc6d692ef6192489d520 to your computer and use it in GitHub Desktop.
require 'benchmark'
require 'ostruct'
#
#% ruby bench_filter.rb
#Rehearsal -------------------------------------------------------
#placeholder 0.120000 0.000000 0.120000 ( 0.128608)
#ruby placeholder 0.550000 0.000000 0.550000 ( 0.548257)
#dynamic placeholder 0.190000 0.000000 0.190000 ( 0.196362)
#---------------------------------------------- total: 0.860000sec
#
# user system total real
#placeholder 0.120000 0.000000 0.120000 ( 0.123801)
#ruby placeholder 0.550000 0.000000 0.550000 ( 0.556790)
#dynamic placeholder 0.180000 0.000000 0.180000 ( 0.189357)
class PlaceholderExpander
attr_reader :placeholders, :log
def initialize(params)
@log = params[:log]
@auto_typecast = params[:auto_typecast]
end
def prepare_placeholders(time, record, opts)
placeholders = { '${time}' => Time.at(time).to_s }
record.each {|key, value| placeholders.store("${#{key}}", value) }
opts.each do |key, value|
if value.kind_of?(Array) # tag_parts, etc
size = value.size
value.each_with_index { |v, idx|
placeholders.store("${#{key}[#{idx}]}", v)
placeholders.store("${#{key}[#{idx-size}]}", v) # support [-1]
}
else # string, interger, float, and others?
placeholders.store("${#{key}}", value)
end
end
@placeholders = placeholders
end
def expand(str, force_stringify=false)
if @auto_typecast and !force_stringify
single_placeholder_matched = str.match(/\A(\${[^}]+}|__[A-Z_]+__)\z/)
if single_placeholder_matched
return @placeholders[single_placeholder_matched[1]]
end
end
str.gsub(/(\${[^}]+}|__[A-Z_]+__)/) {
@placeholders[$1]
}
end
end
class RubyPlaceholderExpander
attr_reader :placeholders, :log
def initialize(params)
@log = params[:log]
@auto_typecast = params[:auto_typecast]
end
# Get placeholders as a struct
#
# @param [Time] time the time
# @param [Hash] record the record
# @param [Hash] opts others
def prepare_placeholders(time, record, opts)
struct = UndefOpenStruct.new(record)
struct.time = Time.at(time)
opts.each {|key, value| struct.__send__("#{key}=", value) }
@placeholders = struct
end
def expand(_str_for_eval_, force_stringify=false)
if @auto_typecast and !force_stringify
_single_placeholder_matched_ = _str_for_eval_.match(/\A\${([^}]+)}\z/)
if _single_placeholder_matched_
return eval _single_placeholder_matched_[1], @placeholders.instance_eval { binding }
end
end
_interpolated_for_eval_ = _str_for_eval_.gsub(/\$\{([^}]+)\}/, '#{\1}') # ${..} => #{..}
eval "\"#{_interpolated_for_eval_}\"", @placeholders.instance_eval { binding }
rescue => e
log.warn "failed to expand `#{_str_for_eval_}`", :error_class => e.class, :error => e.message
log.warn_backtrace
nil
end
class UndefOpenStruct < OpenStruct
(Object.instance_methods).each do |m|
undef_method m unless m.to_s =~ /^__|respond_to_missing\?|object_id|public_methods|instance_eval|method_missing|define_singleton_method|respond_to\?|new_ostruct_member/
end
end
end
class DynamicExpander
def initialize(param)
(class << self; self; end).module_eval {
define_method(:expand) { |tag, time, record|
eval(param)
}
}
end
end
n = 10
tag = 'test_tag'
time = Time.now.to_i
map = {'field_test' => '${message}', 'tag_test' => '${tag}', 'time_test' => '${time}'}
records = []
1000.times { |i|
records << {'num' => i, 'message' => 'hello'}
}
placeholders = {'tag' => tag}
pe = PlaceholderExpander.new({})
rpe = RubyPlaceholderExpander.new({})
dexpanders = {
'field_test' => DynamicExpander.new("record['message']"),
'tag_test' => DynamicExpander.new("tag"),
'time_test' => DynamicExpander.new("Time.at(time).to_s")
}
Benchmark.bmbm do |x|
x.report('placeholder') {
n.times do
records.each { |r|
pe.prepare_placeholders(time, r, placeholders)
new_record = {}
map.each_pair { |k, v|
new_record[k] = pe.expand(v)
}
}
#p new_record
end }
x.report('ruby placeholder') {
n.times do
records.each { |r|
rpe.prepare_placeholders(time, r, placeholders)
new_record = {}
map.each_pair { |k, v|
new_record[k] = rpe.expand(v)
}
}
#p new_record
end }
x.report('dynamic placeholder') {
n.times do
records.each { |r|
new_record = {}
map.each_pair { |k, v|
new_record[k] = dexpanders[k].expand(tag, time, r)
}
}
#p new_record
end }
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment