-
-
Notifications
You must be signed in to change notification settings - Fork 331
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Module#prepend #1826
Module#prepend #1826
Conversation
…n). module/ancestors_spec is green.
… obj.clone. language specs are green.
@Mogztter Could you check please if it works with the Asciidoctor? |
Basically ready for reviewing. |
@iliabylich super great stuff man! I didn't know about the way prepend was implemented in MRI, that's clever! Basically any
is that correct? now proceeding to the review! 😄 |
Yes! |
opal/corelib/kernel.rb
Outdated
|
||
for (i = 0, length = names.length; i < length; i++) { | ||
name = names[i]; | ||
if (name.charAt(0) === '$') { | ||
self_singleton_class_proto[name] = other_singleton_class_proto[name]; | ||
if (name.charAt(0) === '$' && name !== '$$id') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it's worth exposing Opal.is_method
or using the same check?
self_singleton_class.$$const = Object.assign({}, other_singleton_class.$$const); | ||
Object.setPrototypeOf( | ||
self_singleton_class.prototype, | ||
Object.getPrototypeOf(other_singleton_class.prototype) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you considered https://stackoverflow.com/a/23809148 ?
seems to have better performance at startup, but degrades in other cases, probably it's worth it anyway, but I'd like to be sure firefox does not become unbearably slow with proto changes
perf of rake mspec_ruby_nodejs
# prepend
11031 examples, 0 failures (time taken: 13.869999885559082)
56.68 real 55.76 user 2.29 sys
# master
11009 examples, 0 failures (time taken: 10.92799997329712)
54.29 real 52.99 user 2.37 sys
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that's because there's too much dynamic stuff in the RubySpec suite. It create anonymous classes, extends them in runtime, I guess that's the reason. For a static system of modules it seems to be faster.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍🏼 I need to test this on FF/mobile/… with more realistic code, just to be sure, anyway using prototypes makes everything 💯 times cleaner ✨
opal/corelib/runtime.js
Outdated
} | ||
|
||
// Class doesnt exist, create a new one with given superclass... | ||
throw new Error('Broken prototype chain'); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
superclass is fixed and should be easy to cache, probably a good one after this one is done ✅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what do you mean here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean that we should consider storing a ref to the superclass right inside the class, since it can't change in ruby
opal/corelib/runtime.js
Outdated
// If scope is an object, use its class | ||
if (!scope.$$is_class && !scope.$$is_module) { | ||
scope = scope.$$class; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be moved inside the else
of if (scope == null)
opal/corelib/runtime.js
Outdated
@@ -1341,7 +1216,8 @@ | |||
// | |||
// @default [Prototype List] BasicObject_alloc.prototype | |||
// | |||
Opal.stub_subscribers = [BasicObject_alloc.prototype]; | |||
// FIXME |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add some notes on what's to fix?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, we don't need stub_subscribers
anymore (because all bridged classes are inherited from the BasicObject
). The fix is to remove it 😄
I'm getting the following results RubySpec suite1007 static classes, 589 static modules, 299 Chrome:
FF:
Safari:
Boot time78 static classes, 19 static modules, 11 Measured using window.onload = function () {
var loadTime = window.performance.timing.domContentLoadedEventEnd-window.performance.timing.navigationStart;
console.log('Full load time is '+ loadTime / 1000);
} Chrome
FF:
Safari:
|
More benchmarks on V8: function benchmark() {
echo $1 > test.rb
gco master
opal -c test.rb > test.js
echo "Master:"
repeat 5 time node test.js
gco module-prepend
echo "Patch:"
opal -c test.rb > test.js
repeat 5 time node test.js
}
So |
Actually it's not that bad when the ancestors chain is small:
Also I've mirco-benchmarked
|
@iliabylich I'm wondering why FF is so slow compared to Chrome to run the RubySpec suite ? What version are you using ? Also the performance of |
exactly.
From what I understand for a case when we have a huge ancestors chain the time is mostly taken by the part that rebuilds a prototype chain. Maybe JS engines do some recalculations when it happens, I don't know. But anyway, that's a rare case, so I think it's fine. |
True. I will run your branch against the Asciidoctor.js benchmark to see if there's a performance gain :) |
https://jsperf.com/long-proto-chain/1 seems confirmed, the difference with building a proto chain with |
…ry class/module to speedup ancestors calculation.
@elia Did you have a chance to check it? The fix for caching |
@iliabylich merge at will |
Not ready yet.
Includes a massive rewrite of the runtime.js
prototype[method_name] = body
+ updating a list of iclasses + hooks.new Opal.ClassName
.String
is simplified (there are only 5 or 6 failing specs, not sure if it's worth to revert it).RubySpec suite "works for me" (for both node and chrome), going to check other tasks tomorrow.
Also this PR is called
Module#prepend
, so I'm also going to addOpal.prepend_features
, it looks straightforward now.Also I've just realized that we could use
obj instanceof Opal.SomeClass
for classes (for modules there's an iclass in the prototype chain, so it won't work). That's funny.