Skip to content
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

DRY up tappable code for applyPlugins. #34

Closed
wants to merge 1 commit into from

Conversation

samccone
Copy link
Member

@samccone samccone commented Aug 12, 2017

migrate the logic to live in the root function and use a switch
statement to decide when to use call / apply.

Keep the existing functions in place and call though to maintain
backwards compat.

original ref bug:
#33


If you are OK with this change we can delete most of the code in this repo for all of the other functions.

@samccone samccone force-pushed the sjs/dry-code branch 2 times, most recently from ff63ed3 to be8cb64 Compare August 12, 2017 21:03
@@ -53,33 +53,37 @@ Tapable.mixin = function mixinTapable(pt) {
copyProperties(Tapable.prototype, pt);
};

Tapable.prototype.applyPlugins = function applyPlugins(name) {
Tapable.prototype.applyPlugins = function applyPlugins(name, param1, param2) {
if(!this._plugins[name]) return;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without knowing a lot about this code and how it's being used, I'd suspect that this this._plugins[name] is not going to be monomorphic (i.e. name will be different and probably this._plugins too). So repeating this load is going to be the expensive part.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, based on this benchmark, I'd strongly suggest to go for F.p.apply and rest parameters instead, as indicated by the results for Chrome Canary:

image

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I was curious about Chrome Stable (V8 6.0.286.52, same as Node 8.3, and this code is running on Node) and the results are bit different:

screen shot 2017-08-13 at 09 15 45

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jep, just checked Chrome 61 (which is what Node 8 will eventually be based on in the end), and same picture there:

Chrome 61 results

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: Ran this gist with latest Node 8.3.0 locally and I do indeed see results similar to what I observed with Canary:

Node 8.3.0 results

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR updated with optimized version

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see to performance benefit for the method with variable number of arguments.

But I don't see the benefit for the methods with fixed number of arguments (i. e. applyPlugins2). They are manually optimized for a specific number of arguments. Most "hot" hooks use them.

image

Anyway, I would love to take a more radical approach. This whole package is a bit old. What about a new major version with more performance and more features.

  • Never use arguments, compile fixed arguments function.
  • Create a function per hooks which makes it monomorphic.
  • Maybe something magic like new Function(`function x(${args}) ...`)
  • Different apply function depending on number of registered plugins
    • no plugins: noop apply for max performance
    • special version to 1 plugin: optimized
    • general version for more plugin
  • You need to register hooks before using them. You need to specify number of arguments and kind (sync/async/parallel/waterfall).
  • Instead of compiler.plugin("before-run", ...) compiler.hooks.beforeRun(...) to allow have typings for these hooks.
  • Each plugin can have a name compiler.hooks.beforeRun("HotModuleReplacementPlugin", () => ...)
  • Insert plugins at specify locations instead of always to the end. hooks.beforeRun({ name: ..., before: "HotModuleReplacementPlugin" }, () => ...)
  • Maybe a middleware api, if this doesn't affect performance: hooks.beforeRun.intercept(...)
    • Would be great of the progress display and profiling.
  • The plugin(...) method can be provided for backward compatibility for plugins.
    • We may generate plugin name from stack trace (depending on performance cost for this)
  • The old applyPluginsXXX methods can also be offered for backward compatibility, but trigger a deprecation.
  • Using a unknown hook name in the plugin or applyPluginsXXX automatically register a compat hook which handle them.
  • The compatibility methods convert dash-case to camelCase.
  • This would be a big breaking change, but I guess acceptable with the compatibility layer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created a new issue for this: #35

lib/Tapable.js Outdated
var plugins = this._plugins[name];
for(var i = 0; i < plugins.length; i++)
plugins[i].apply(this, args);
};
switch (arguments.length) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have a benchmark for this? I.e. what are the likely cases?

@samccone samccone force-pushed the sjs/dry-code branch 2 times, most recently from 40ec461 to af6628a Compare August 15, 2017 02:54
@samccone
Copy link
Member Author

@TheLarkInn PTAL when you have a moment. thank you!

migrate the logic to live in the root function and use a switch
statement to decide when to use call / apply.

Keep the existing functions in place and call though to maintain
backwards compat.

original ref bug:
@sokra
Copy link
Member

sokra commented Dec 11, 2017

new major version uses a better system to avoid using arguments and generate a function per hook

@sokra sokra closed this Dec 11, 2017
@samccone samccone deleted the sjs/dry-code branch December 11, 2017 14:30
@samccone
Copy link
Member Author

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants