solnic / coercible

Powerful, flexible and configurable coercion library. And nothing more.
MIT License
137 stars 16 forks source link

Raise less exceptions in Coercer::String #15

Closed jstepien closed 8 years ago

jstepien commented 10 years ago

This patch reduces the number of exceptions raised internally in Coercer::String when dealing with incoercible values. The motivation for this change is the cost of raising an exception.

Instead of raising an exception in the private #to_numeric method the COERCION_FAILURE object is returned. This reduces the number of exceptions from 2 to 1 in case of #to_float and from 3 to 2 in case of #to_integer.

Consider a following benchmark evaluating the influence of this patch.

require 'benchmark'
require 'coercible'

def test_case(coercer)
  100_000.times do
    begin
      coercer[String].to_integer "asdf"
    rescue
    end
  end
end

coercer = Coercible::Coercer.new
raise unless 0.5 == coercer[String].to_float("0.5")

# warm up before measurements
test_case(coercer)
bm = Benchmark.measure { test_case(coercer) }
p bm

On JRuby 1.7.10 running on OpenJDK 64-Bit Server VM 1.7.0-u60-b01-20131204 this patch improves the benchmark result by 33%. It's exactly one third because we got rid of 1 out of 3 exceptions; that's how expensive they are!

If you replace #to_integer with #to_float the improvement is even more apparent; it's nearly 50%. The reason is removal of 50% of exceptions.

Notice that if we would take further steps in this direction and reduce the number of exceptions raised in #to_integer to 1 we could bring the total improvement to 66%.

Interestingly enough, on MRI 2.1.1 this patch brings 10% improvement in case of #to_integer and nearly 30% in case of #to_float. Raising exceptions isn't that expensive there as on JVM but it still comes with a noticeable cost.

I did my best to maintain full compatibility of the public API.