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

node: improve performance of nextTick #985

Closed
wants to merge 1 commit into from

Conversation

trevnorris
Copy link
Contributor

Couple micro optimizations to improve performance of process.nextTick().
Removes ~80ns of execution time.

Also added small threshold to test that allows timer to fire early on
the order if microseconds.

R=@bnoordhuis

@@ -340,7 +340,8 @@
function _tickCallback() {
var callback, threw, tock;

scheduleMicrotasks();
if (microtasksScheduled)
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the point? It is checked inside already

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Test for yourself. It removes a third of execution time:

'use strict';
const ITER = 1e6;
let i = ITER;
let t = process.hrtime();

(function runner() {
  if (--i > 0) process.nextTick(runner);
  else printTime(process.hrtime(t));
}());

function printTime(t) {
  console.log(((t[0] * 1e9 + t[1]) / ITER).toFixed(1) + ' ns/op');
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, I thought it was if (!microtasksScheduled). So it's basically the same as if you remove scheduleMicrotasks

Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder why no tests break. This does though

setTimeout(function() {
  process.nextTick(function(){});
  Promise.resolve().then(function() {
    console.log('hi');
  });
}, 10);

setTimeout(function(){}, 1000);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah. I thought V8 would have been a little better at inlining the conditional of the function call, but it does seem to make enough difference to require placing the conditional here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@vkurchatkin How is it breaking? Like, 'hi' doesn't display?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah, wow. totally missed what you were saying about the conditional. let me fix it and push up a fix. one moment.

Copy link
Contributor

Choose a reason for hiding this comment

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

Displays after 1000ms timeout. I'm going to add tests for this branch of execution.

@trevnorris trevnorris force-pushed the faster-nexttick branch 2 times, most recently from 25a89e5 to b1a861a Compare March 2, 2015 19:44
@trevnorris
Copy link
Contributor Author

@vkurchatkin I've updated the name of PObj and fixed the other issue. Have reduced execution time from ~120ns to ~60ns.

@vkurchatkin
Copy link
Contributor

This is still wrong:

if (microtasksScheduled)
       scheduleMicrotasks();

});

tickInfo[kLength]++;
process.nextTick(runMicrotasksCallback);
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe it's better to use nextTick instead of process.nextTick? don't like when we rely on globals being untouched

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good idea. done.

@trevnorris trevnorris force-pushed the faster-nexttick branch 2 times, most recently from 020dd4a to c26204e Compare March 2, 2015 20:00
@trevnorris
Copy link
Contributor Author

@vkurchatkin thanks. forgot to force push my latest change. also fixed using process.nextTick().

@trevnorris trevnorris force-pushed the faster-nexttick branch 2 times, most recently from 2411e99 to efe37ec Compare March 2, 2015 20:11
@trevnorris
Copy link
Contributor Author

@vkurchatkin Just made a change to move scheduling the microtask queue processor to the end of the tick callback. makes a small functional change in the way it works, but all callbacks will still be executed. mind verifying?

@vkurchatkin
Copy link
Contributor

Can't verify it right now but it looks like it breaks intended behavior completely. When we are done with tick queue it's too late to schedule microtaks because: a) they should run on nextTick queue b) we should be preparared to go on with nextTicks scheduled inside microtasks

@trevnorris
Copy link
Contributor Author

@vkurchatkin hm. all tests are currently passing. i'll try to write up additional tests to show what you're talking about, but I'll most likely need some help to make sure they're correct.

@vkurchatkin
Copy link
Contributor

@trevnorris tests probably don't cover these cases. When there are no nextTick callbacks microtasks are executed directly from MakeCallback. My oversight (that there are no tests for that).

Couple micro optimizations to improve performance of process.nextTick().
Removes ~60ns of execution time.

Also added small threshold to test that allows timer to fire early on
the order if microseconds.
@trevnorris
Copy link
Contributor Author

@vkurchatkin I've updated the PR to execute the microtask queue only when the queue is totally depleted, but will also return and continue execution if additional things were added. This is in line with what my prior attempt meant to do. PTAL.

@vkurchatkin
Copy link
Contributor

LTGM. this is something I wanted to do in the first place, but decided to choose safer solution

@trevnorris
Copy link
Contributor Author

Heh. Then I'll be happy to take the blame in the future if it breaks. :)

trevnorris added a commit that referenced this pull request Mar 3, 2015
Couple micro optimizations to improve performance of process.nextTick().
Removes ~60ns of execution time.

Also added small threshold to test that allows timer to fire early on
the order if microseconds.

PR-URL: #985
Reviewed-By: Vladimir Kurchatkin <[email protected]>
@trevnorris
Copy link
Contributor Author

@vkurchatkin Thanks for the review. Landed in e0835c9.

@trevnorris trevnorris closed this Mar 3, 2015
@trevnorris trevnorris deleted the faster-nexttick branch March 3, 2015 20:47
@rvagg
Copy link
Member

rvagg commented Mar 3, 2015

Do we have any benchmark numbers on this? it's always good to communicate via numbers, even if they are very rough, keep that in mind whenever adding perf changes because people love seeing it but when it's just a vague "improve performance" then it's hard to make an assessment about how it impacts you.

@trevnorris
Copy link
Contributor Author

@rvagg Here's the script I used to test:

'use strict';
const ITER = 1e6;
let i = ITER;
let t = process.hrtime();

(function runner() {
  if (--i > 0) process.nextTick(runner);
  else printTime(process.hrtime(t));
}());

function printTime(t) {
  console.log(((t[0] * 1e9 + t[1]) / ITER).toFixed(1) + ' ns/op');
}

Similar to benchmark/misc/next-tick-depth.js.

@rvagg rvagg mentioned this pull request Mar 4, 2015
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.

9 participants