-
-
Notifications
You must be signed in to change notification settings - Fork 753
WIP: Refactor metadata #1007
WIP: Refactor metadata #1007
Conversation
Rather than instantiating it and calling `process`, it makes more sense to pass in all the args into one method. This prepares the way for that.
- It's best to avoid subclassing core types like Hash. - Remove runtime module extensions, since that blows MRIs method caches and has an affect on perf. - Instantiation was a two part process: `.new` followed by `process`. Conceptually, these belong together, so switch to using a single method for instantiation. Two gotchas I discovered: - The `include` matcher didn't initially work properly with `Metadata`. It uses an `is_a?(Hash)` check to know how to treat the object. To solve this, we override `is_a?` to return true for Hash. - `dup` didn't work as expected. `dup` is used to get a copy that can be modified without affecting the original; however, `dup` was simply duping the wrapping object, and each retained a reference to the same wrapped hash, causing changes on one to show up on the other. I override `dup` and `clone` to address this.
|
My own benchmarks... |
|
I've run into performance issues (normally negligible in total usage) with ruby delegates before, a quick bench mark show that this is still an issue and is probably the cause of any slow down you may be seeing, I seem to remember it was more pronounced on |
Can you post said benchmark? I'd like to see for myself. |
|
I pushed a commit with the script I used and the outcomes for me, but I'll repost the outcomes below |
|
Did you get a chance to look at the benchmark? WDYT about #1008 ? |
|
I haven't had a chance yet. I have some implementation concerns about #1008, and I want to dig into this more before deciding what to do. |
|
Fair. I was just gently nudging you ;) |
|
Is "It's best to avoid subclassing core types like Hash." based on Steve Klabnik's recent blog? I feel like it's not clear how much you can realistically expect to run into the weirdness he discovered there (I believe his post even says that it works well 99% of the time). In any case, I wonder how much faster TLDR; Subclassing Hash may be ok here. |
|
@soulcutter -- the stuff Steve brings up is part of it, but I had been thinking about doing this long before that blog post. In general, I nearly always favor composition over inheritance, especially when dealing with core types. More than any specific problems we've had (or not had), I think "don't subclass core types" is sound advice and I'd like rspec's code to be a good "role model" in this regard, so to speak. That said, I took a look at the delegate logic and I can see why it's so much slower: https://github.com/ruby/ruby/blob/835c555f7f137746eb6f7cd32bd05a386852fdbe/lib/delegate.rb#L319-L328 One delegation results in a couple extra method sends (e.g. one to So this approach isn't going to work. I'm going to close this to continue the discussion on #1008. |
|
I generally agree about composition vs inheritance, but I was giving a pragmatic perspective there :) Any reason to leave this code branch around? |
|
#1008 pulls into |
Convert Metadata to use composition rather than inheritance.
caches and has an affect on perf.
.newfollowed byprocess.Conceptually, these belong together, so switch to using a single
method for instantiation.
Two gotchas I discovered:
includematcher didn't initially work properly withMetadata.It uses an
is_a?(Hash)check to know how to treat the object.To solve this, we override
is_a?to return true for Hash.dupdidn't work as expected.dupis used to get a copy that can bemodified without affecting the original; however,
dupwas simplyduping the wrapping object, and each retained a reference to the same
wrapped hash, causing changes on one to show up on the other. I override
dupandcloneto address this.However...this seems to have a noticeable negative perf impact, in spite of the fact I expected it to have a positive perf impact (due to no longer using module extensions at run time). Here are 3 runs of the test suite against this branch:
Compare that to master:
I'm not sure why. This seems like a better design to me, but I don't want to merge it if it's going to have that much of an affect on perf. There may be a way to salvage it though.
Thoughts?
/cc @alindeman @JonRowe @soulcutter @samphippen