-
-
Notifications
You must be signed in to change notification settings - Fork 34k
3x faster setImmediate #6436
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
3x faster setImmediate #6436
Changes from 1 commit
44243ad
d005b81
ad3bcb8
1f82a29
49b2611
c12ba47
6dcbd83
197aac2
4b54cd7
8179d32
77e0443
0e75f1f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
Save the setImmediate callback arguments into an array instead of a closure, and invoke the callback on the arguments from an optimizable function. 60% faster setImmediate with 0 args (15% if self-recursive) 4x faster setImmediate with 1-3 args, 2x with > 3 seems to be faster with less memory pressure when memory is tight Changes: - use L.create() to build faster lists - use runCallback() from within tryOnImmediate - create immediate timers with a function instead of new - just save the arguments and not build closures for the callbacks
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -515,9 +515,13 @@ function processImmediate() { | |
| immediate = L.shift(queue); | ||
| domain = immediate.domain; | ||
|
|
||
| if (!immediate._onImmediate) | ||
| continue; | ||
|
|
||
| if (domain) | ||
| domain.enter(); | ||
|
|
||
| immediate._callback = immediate._onImmediate; | ||
| tryOnImmediate(immediate, queue); | ||
|
|
||
| if (domain) | ||
|
|
@@ -538,7 +542,8 @@ function processImmediate() { | |
| function tryOnImmediate(immediate, queue) { | ||
| var threw = true; | ||
| try { | ||
| immediate._onImmediate(); | ||
| // make the actual call outside the try/catch to allow it to be optimized | ||
| runCallback(immediate); | ||
| threw = false; | ||
| } finally { | ||
| if (threw && !L.isEmpty(queue)) { | ||
|
|
@@ -552,56 +557,66 @@ function tryOnImmediate(immediate, queue) { | |
| } | ||
| } | ||
|
|
||
| function runCallback(timer) { | ||
| var argv = timer._argv; | ||
| var argc = argv ? argv.length : 0; | ||
|
||
| switch (argc) { | ||
| // fast-path callbacks with 0-3 arguments | ||
| case 0: | ||
| return timer._callback(); | ||
| case 1: | ||
| return timer._callback(argv[0]); | ||
| case 2: | ||
| return timer._callback(argv[0], argv[1]); | ||
| case 3: | ||
| return timer._callback(argv[0], argv[1], argv[2]); | ||
| // more then 3 arguments run slower with .apply | ||
| default: | ||
| return timer._callback.apply(timer, argv); | ||
| } | ||
| } | ||
|
|
||
| function Immediate() { } | ||
|
|
||
| Immediate.prototype.domain = undefined; | ||
| Immediate.prototype._onImmediate = undefined; | ||
| Immediate.prototype._idleNext = undefined; | ||
| Immediate.prototype._idlePrev = undefined; | ||
|
|
||
| function createImmediate(callback) { | ||
| return { | ||
|
||
| _idleNext: null, | ||
| _idlePrev: null, | ||
| _callback: callback, | ||
| _argv: null, | ||
|
||
| _onImmediate: callback, | ||
| domain: process.domain, | ||
| }; | ||
| } | ||
|
|
||
| exports.setImmediate = function(callback, arg1, arg2, arg3) { | ||
| if (typeof callback !== 'function') { | ||
| throw new TypeError('"callback" argument must be a function'); | ||
| } | ||
|
|
||
| var i, args; | ||
| var len = arguments.length; | ||
| var immediate = new Immediate(); | ||
| var immediate = createImmediate(callback); | ||
|
||
|
|
||
| L.init(immediate); | ||
|
|
||
| switch (len) { | ||
| switch (arguments.length) { | ||
| // fast cases | ||
| case 0: | ||
| case 1: | ||
| immediate._onImmediate = callback; | ||
| break; | ||
| case 2: | ||
| immediate._onImmediate = function() { | ||
| callback.call(immediate, arg1); | ||
| }; | ||
| immediate._argv = [arg1]; | ||
| break; | ||
| case 3: | ||
| immediate._onImmediate = function() { | ||
| callback.call(immediate, arg1, arg2); | ||
| }; | ||
| immediate._argv = [arg1, arg2]; | ||
| break; | ||
| case 4: | ||
| immediate._onImmediate = function() { | ||
| callback.call(immediate, arg1, arg2, arg3); | ||
| }; | ||
| immediate._argv = [arg1, arg2, arg3]; | ||
| break; | ||
| // slow case | ||
| default: | ||
| var len = arguments.length; | ||
|
||
| args = new Array(len - 1); | ||
| for (i = 1; i < len; i++) | ||
| args[i - 1] = arguments[i]; | ||
|
|
||
| immediate._onImmediate = function() { | ||
| callback.apply(immediate, args); | ||
| }; | ||
| immediate._argv = args; | ||
| break; | ||
| } | ||
|
|
||
|
|
@@ -610,9 +625,6 @@ exports.setImmediate = function(callback, arg1, arg2, arg3) { | |
| process._immediateCallback = processImmediate; | ||
| } | ||
|
|
||
| if (process.domain) | ||
| immediate.domain = process.domain; | ||
|
|
||
| L.append(immediateQueue, immediate); | ||
|
|
||
| return immediate; | ||
|
|
||
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.
Are we sure we don't do this optimization anywhere else in the code more generically? It sounds like something that would go in
util. If we don't then LGTM.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 use this construct in other projects, and found it hard to make generic;
the call semantics don't cover all uses cases well. I have 3 variants,
this version I used here is a 4th.
That being said, I agree, an optimized call invoker is a nice utility to have.