-
Notifications
You must be signed in to change notification settings - Fork 30.5k
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
timers: optimize same-tick unref #8372
Conversation
@misterdjules how do you feel about this? It should be possible to shim |
Thank you for the heads up @Fishrock123, I appreciate it! I don't understand what the goal of this PR is. It's likely that I'm missing something obvious, so please consider the questions below as genuine questions to help me understand what this PR is about, and not as rhetorical questions aimed at dismissing it. In the title, it mentions that it optimizes something, but I don't understand what use case or code path is optimized, and in what way. So my first question is: what problem does this PR solve? If this is about making some code path(s) run faster, are there some benchmarks somewhere that we can take a look at? It also refers to #6699, but my understanding of #6699 is that it is about being able to differentiate between internal/external timers correctly. How is this PR related to that? Lastly, the second comment of this PR mentions " shim[ing] _handle so as to make sure this does not break anything.". How does this PR breaks anything related to using the Thank you! |
This makes same-tick e.g. // optimized
setTimeout(() => {}, 100).unref()
// not optimized
const time r= setTimeout(() => {}, 100)
setImmediate(() => {
timer.unref()
})
It is not. It is related to the discussions therein. |
lib/timers.js
Outdated
function insertNT() { | ||
insertNTScheduled = false; | ||
let timer; | ||
while (timer = queuedTimers.shift()) { |
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.
If this is about performance, wouldn't a linked list make more sense 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.
Doh, yes.
The logic behind this revolves around the following: 1. A timer will never fire before nextTick. - Thus it is ok to schedule it during nextTick. 2. Timer properties are unimportant until scheduled. See discussion in nodejs#6699 for more background.
6f1a877
to
620412b
Compare
Updated with @ronkorving's suggestion.
This currently does not ensure there is always a |
this.owner._onTimeout(); | ||
if (!this.owner._repeat) | ||
this.owner.close(); | ||
} | ||
|
||
function createOwnHandle() { | ||
var now = TimerWrap.now(); |
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.
extremely minor nit: this could/should be const
Just to make sure I understand this correctly: is one goal of this PR to make unrefed external timers use If so, what prevents us from doing that even in the following example you mentioned previously:
? It seems to me that this PR tries to achieve two things that are orthogonal to each other:
Is that correct? If these two points are orthogonal, I would suggest implementing them in two separate PRs. I would also like to see numbers that illustrate the impact of point 2) on performance. |
Effectively.
@misterdjules if you know how to do that arbitrarily delayed linked-list insert in less-than-linear ( Note: This is the problem I have been trying to work around the entire time. Edit: even with a sorting algo, you cannot insert or view arbitrary places in the list without traversing it.
I will get numbers if it comes down to the line but the only thing significant added is the |
Right, I wasn't aware of the trade-offs you had in mind, but that clarifies things, thanks! Would unrefing external timers be called often enough in most use cases that the insertion time would be a problem? It was in the case of internal (and by definition unrefed) timers because these timers were restarted and thus re-inserted very frequently (like on every I/O event on a given Coming up with sample code and benchmarks that represents the typical use cases we have in mind could help here. I'm asking these questions because my first impression is that the inconsistency between external unrefed timers that share a
I realize now that the use of a |
Correct. It's like the Promises &
I really have no idea, it would also be very hard to identify in the wild, buried in async layers. It could potentially tank perf pretty hard though in any application using lots of TCP connections. Edit: hard may still be minor, but it could become significant in any perf profiles. I originally was thinking of having something like
It would probably look exactly like a TimerWrap, except it wouldn't behave 100% like one under the hood, i.e. closing it would just close that timer and not the entire pool. |
That wasn't clear from my original question: what I wanted to ask is whether unrefing a timer asynchronously is a common use case. If not, then it might be OK to have such I wanted to experiment with another approach to making unrefed timers use The result is a WIP commit that still needs to deal with the issue you mentioned with the |
The indeterminism makes me hope not. cc @brycebaril who is the only person I know to use
Uh, I guess you can unref some time from a downstream library you may be using? I can't think of anything else really.
Huh. That is an interesting but somewhat more complex approach. Am I correct in saying that the logic behind this revolves around the fact that unrefed handles don't matter if there are still refed handles? I suppose the big question would be if refing and unrefing a handle potentially a large number of times poses any cost on perf and/or anything else. cc @trevnorris? |
Pretty much every time I've used it, the In either case typically the total number of live timers at any given time shouldn't be that large for most use-cases. Any timer-centric use-case beyond that is probably a lot less likely to have unref'd timers. |
That is correct. |
c133999
to
83c7a88
Compare
@Fishrock123 ... still interested in this? |
Ping @Fishrock123 |
@BridgeAR ... given the lack of any activity, I think it's safe just to close this. @Fishrock123 can reopen if it's something he's still interested in pursuing |
I am relatively certain that I actually ran into this issue once and that I removed the refing / unrefing again because of the immense performance penalty. Therefore I think it would still be nice to get this in. That is why I still had hope 😄 |
Yeah, it's a shame these older PRs weren't followed up on |
the discussion here is valuable and should be moved into an issue |
Moving to the following issue I've created, which should really have been done instead of just hands-off closing this. How will anyone ever know that they could potentially pick this up otherwise? |
Checklist
make -j4 test
(UNIX), orvcbuild test nosign
(Windows) passesAffected core subsystem(s)
timers
Description of change
The logic behind this revolves around the following:
(Thus it is ok to schedule it during nextTick.)
See discussion in #6699 for more
background.
CI: https://ci.nodejs.org/job/node-test-pull-request/3919/