Closed cben closed 7 years ago
@cben interesting, another option is to cache methods defined on the singleton_class but that's probably not worth the headache. I think your change should work. I"m not sure why the test is failing though.
Not sure about the Travis failure. I'm geting the same failure
expected #<Enumerator: #<RecursiveOpenStruct foo="foo", bar=:bar>:each_pair> to match #<Enumerator: {:foo=>"foo", :bar=>:bar}:each_pair>
on master locally, but I see master passed on Travis (and bundler used same dependency versions as I get locally). I can't find info on what rspec does when you simply match enumerators.
I'm pretty sure that the Travis failure is due to RSpec 3.5.x vs 3.6.x changing something. I updated my Gemfile.lock locally, and the test suite now fails on master.
I don't currently have any benchmarks set up, but feel free to add something and/or suggest a good service for tracking how performance benchmarks change (although I wouldn't rely on metrics gathered from Travis' test runs for benchmarks due to potential variance in the underlying VM).
I just pushed a minor change to the failing spec to ROS's master that seems to address the issue. Will merge your change in a moment, and then I'll release 1.0.5.
Primed by seeing @jrafanie's *O(N2) fix #52, I was byebug-ing through my code into
new_ostruct_member
and noticed another O(n2)*:I say small n because it's number of direct subelements not whole tree size. However I'm a bit shocked that it makes even tiny 1-element ROS 2.5 times faster(!) — probably because
.methods
includes 85 inherited methods even before new_ostruct_member adds any. (notemethods(false)
below being fast for small hash but still *O(n*2) for large hash). This is perhaps why #52 did not see such a big improvement...Microbenchmark (using same data @jrafanie used, large hash has 579 keys, ends up with 1737 methods): https://github.com/cben/recursive-open-struct/blob/benchmark-methods-include/benchmark.rb Essentially creates ROS from a hash, then 5 times doing
.foo
read access on all fields.This includes several ways I've tried to do this check, some buggy approximations. I'm not sure this check it's needed at all (
false
is without) — AFAICTnew_ostruct_member
is only called frommethod_missing
so can assume we don't have the method yet — butmethod_defined?
is negligibly cheap.I have some more ideas (though not enough time) for RecursiveOpenStruct and/or OpenStruct performance. Are there any benchmarks you use? cc @sferik
method_missing
every time! For my microbenchmark, not creating methods is 1.5~2 faster than any of the above:env HERESY=1 SHARE=1 ruby benchmark.rb
=> https://benchmark.fyi/y, https://benchmark.fyi/z, https://benchmark.fyi/A