Delegators performance in Ruby (and Logstash)
- Dominant language
- Java
- Stars
- 14.9k
- Forks
- 3.5k
- Avg merge
- 19h 14m
- Merged PRs (30d)
- 63
Description
This project uses delegators for a couple of things: `LogStash::Timestamp` (wraps around Time) and `LogStash::StringInterpolation` (used for template evaluation when doing sprintf on events).
While this is a very useful technique to decorate objects, underneath Delegate will use `method_missing` to wire up the delegator and the delegated object. This means that all delegated behaviour will be slower than native calls.
How slow? I've tried comparing a delegator around Time, Time itself and a class that defines the delegations explicitly:
``` ruby
require 'time'
require 'forwardable'
require 'benchmark/ips'
class Tempo
extend Forwardable
def_delegators :@time, :<=>, :+, :to_i, :to_f, :tv_usec, :year
def initialize(time)
@time = time
end
end
class FastTempo
def initialize(time)
@time = time
end
def <=>(arg); @time.<=>(arg); end
def +(arg); @time.+(arg); end
def to_i; @time.to_i; end
def to_f; @time.to_f; end
def tv_usec(*args); @time.tv_usec(*args); end
def year; @time.year; end
end
SIZE = 1000000
times_array = SIZE.times.map {|i| Time.at(1441621030 - rand(100000000)) }
tempos_array = times_array.map {|i| Tempo.new(i) }
fast_tempos_array = times_array.map {|i| FastTempo.new(i) }
Benchmark.ips do |x|
# Configure the number of seconds used during
# the warmup phase (default 2) and calculation phase (default 5)
x.time = 30
x.warmup = 10
x.report("times_array") do
array = times_array
i = rand(100000)
array[i] <=> array[i+1]
array[i] + array[i].to_f
array[i].to_i
array[i].year
array[i].tv_usec
end
x.report("tempos_array") do
array = tempos_array
i = rand(100000)
array[i] <=> array[i+1]
array[i] + array[i].to_f
array[i].to_i
array[i].year
array[i].tv_usec
end
x.report("fast_tempos_array") do
array = fast_tempos_array
i = rand(100000)
array[i] <=> array[i+1]
array[i] + array[i].to_f
array[i].to_i
array[i].year
array[i].tv_usec
end
end
```
The results are not surprising:
```
% ruby -v
jruby 1.7.19 (1.9.3p551) 2015-01-29 20786bd on Java HotSpot(TM) 64-Bit Server VM 1.8.0_20-b26 +jit [darwin-x86_64]
% ruby benchmark_delegator.rb
Calculating -------------------------------------
times_array 42.783k i/100ms
tempos_array 27.153k i/100ms
fast_tempos_array 40.956k i/100ms
-------------------------------------------------
times_array 653.700k (± 4.5%) i/s - 19.595M
tempos_array 344.911k (± 4.4%) i/s - 10.345M
fast_tempos_array 540.825k (± 4.4%) i/s - 16.219M
```
Using the Forwardable delegation is 47% slower than native calls, and using explicit delegation is 17% slower.
Since both uses of Forwardable are on the critical path of performance for Logstash it's advisable to tackle this in a near future.
Contributor guide
Assessment
This issue has not been assessed yet.