-
Notifications
You must be signed in to change notification settings - Fork 47
Performance impact of async_hooks #181
Comments
There's been a related discussion in nodejs/promises#31 earlier this year, which yielded a couple of interesting improvements and insights. Pinging @nodejs/performance @nodejs/async_hooks |
Particularly about async_hooks and promises or promises in general? For async_hooks and promises - I think a server-application benchmark could be good here - with contexts through async functions through several layers of calls - I'd then listen to For promises in general there are at least 2-3 good ideas in that thread that can get native promises finally faster than userland alternatives like bluebird. Namely skilling the microtask queue when possible and inlining calls - not to mention async iterators that aren't fast yet but could revolutionize programming in Node.js if they were. |
Improving promise performance in general is also a good idea, but this particular issue is primarily about the impact of As for promises in general and in particular I think @mcollina had some comments about that before. Can we derive a simple representative performance test from fastify to get things going? |
Specifically, regarding
I've gotten many real-life complains from both APM providers and other I'm guessing this would improve performance a lot, as we would no longer have to wrap the promise object itself to get notified about garbage collection. Essentially we could just create an unreferenced basic object on Extra feature requests that are not performance related.
/cc @addaleax |
@AndreasMadsen Thanks for the feedback, that's very interesting. These seem to be more on the feature request side.
Do you have a suggestion/intuition here how to do that?
So you're saying that by changing the API and adding machinery to implement |
Often promises are only "referenced" once, either by function wait(ret, ms) {
// hint: the promise is directly returned
return new Promise(function (resolve, reject) {
setTimeout(() => resolve(ret), ms);
});
}
function main() {
// hint: the promise is directly used in `await` without being assigned to a variable
await wait(1, 10) // after this line the user expects the `destroy` hook to emit
await wait(2, 10) // same for this promise
await wait(3, 10) // same for this promise
}
Yes, that would be my guess. Wrapping and unwrapping the |
cc @jaro-sevcik |
If V8 could detect that with escape analysis and optimize the return value of that function to an entirely different implementation that would be awesome. Fast libraries all hack this by having the single listener case optimized - but an engine that can prove it and avoid allocating extra stuff would be awesome. In terms of promise hooks I suspect it would also simplify things a lot for the common case. |
On the benchmarks side, we can also consider Hapi v17, which is completely async-await based. The problem with real-world code is that it typically involves a database, and that makes it very hard to measure small improvements. IMHO we should measure:
I fear this would have to be a synthetic application. |
This is what Doxbee did (the bluebird benchmark suite) which you're familiar with - we can fork and extend it. |
@benjamingr I think we are all more concern on overall impact of async await rather than a comparative measurement between implementations. Plus, I think we should focus on overall performance including a web framework, a database and HTTP requests rather than just the promise/async await layer. |
Thanks @AndreasMadsen, @benjamingr and @mcollina! This is definitely interesting.
This seems surprising to me. I would think that calling out to the javascript hook defined by the user from C++ to be the most expensive part. Have you tried benchmarking with and without the wrapping?
The way escape analysis works is by figuring out if a given allocation is unobservable. But, we lose all that with PromiseHooks as the
@mcollina I agree, 💯. The hard part would be to try and get reproducible results from such a benchmark though. @AndreasMadsen -- Can you link to a real world usage of async hooks? How are developers using async hooks? Is it for async stack traces? Is the |
@mcollina My gut feeling is that we will need some kind of micro-benchmarks to drive individual work items. Plus real-world code to see overall impact and find appropriate areas worth looking into. @benjamingr Escape analysis could indeed eliminate promises (sometimes), but with promise hooks that becomes impossible, since every promise already escapes/leaks to the hooks. |
I admit, I have not done a full matrix comparison. But this was the observation when we initially added the wrapping. It also depends on what hooks are used. I remember that we have some numbers for this but I can't find them.
I can't think of any that actually use the
That and continues local storage. Hopefully, we will see it being used as a profiling tool too.
It is not implemented by
Very. |
I've heard that |
I think what we're measuring I tend to agree with database/web framework and http requests - but we need to make sure those are as thin as possible so we don't end up with an implementation that is fine "for the average case" but completely breaks for apps that have a harder performance requirement. What about a benchmark that does:
In general, it's mostly a scope question of whether Node.js should optimize for "the average app" or for "performance sensitive apps".
I'm not sure I follow - so I'll clarify my request/suggestion. Escape analysis can (probably) greatly optimize most of the modern usage of promises in async/await where we're awaiting a direct invocation of an async function - and use much simpler promise code. async function bar() {
}
(async function foo() {
// bar is also an async function, this call only has one listener and doesn't escape
// this is, presumably "the common case".
await bar();
})(); In this case, we don't actually need to allocate a real promise object - we can continue the execution of foo when If we can prove other nice properties for I realize this is far from the actual implementation but the benefit is also huge and it doesn't sound "too crazy" (I hope :)). For my specific request: when/if this is optimized, you can inline |
@benjamingr The thing with promise hooks (as required by |
@bmeurer As said, we don't actually need the |
That's not how it's implemented currently. I think I need to look at the Node core side again. Don't you need to slap ids onto the |
@bmeurer yes we do. If Our |
@AndreasMadsen But we still need to materialize the |
This is what I'm saying! We are only using it propagating context, so if |
@AndreasMadsen Staring at the
@gsathya does that make sense to you as well? Do we have other users of the |
Correct. The resource = {
promise: JSPromise,
// lookup parent asyncId on the internal field of the parent PromiseWrap
parentId: parentJSPromise.[[PromiseWrap]].[[asyncId]]
}
On our side, it would be API breaking but I don't see a big issue here. What would be missing is the
yes. It would require two things:
These are the only critical things we use the
yes We would need at least one id ( |
Angular current provide a currently in
I am still looking for walk around, but it seems to be a |
Note that async-hooks are still experimental, so the cost of making a semver breaking change is not that high, specially if it has significant benefits. Regardless, if V8 was to implement this today, this would only get into Node 10, a semver major release. Whether or not the change can be backported to Node 8 and 9 would depend on the complexity rather than semver-ness of the impact to async-hooks API. |
@ruimarinho Oh that's pretty bad. Was it due to Promise heavy code? Or just old fashioned callback style? Also what kind of software did you try to deploy using |
@bmeurer yes - heavy Promise code (using async/await). Full stack http request server, including redis and database connections. We are going to try enabling |
@ruimarinho Ok, if you're using bluebird promises, that's going to be slower than native promises already by itself. |
Accidentally commented on the TSC thread instead of this one. So for future reference, I also did a test run of simple The And to provide even more data for the discussion: The performance drop also increases with the number of hooks being used (maybe not unsurprising). For example for the |
Very interesting data @bmeurer, thanks for the insights on this subject. We definitely hit the
Does this hold true even even when |
In that case you'll probably use bluebird promises for the |
Let me know whether it is not relevant and I should delete my post, but I experienced quite a bad increase in CPU usage when I activated |
I would recommend to check out the latest Node 8, Node 10 and Node 11, as the runtimes that are used in the blog post are quite old, or not supported anymore (Node 9). There should have been some improvement in the area. Overall |
Thanks. I did try the latest Node 11 (the last graph in the blog post) with
no difference.
…On Thu, Nov 1, 2018, 11:10 AM Matteo Collina ***@***.***> wrote:
I would recommend to check out the latest Node 8, Node 10 and Node 11, as
the runtimes that are used in the blog post are quite old, or not supported
anymore (Node 9). There should have been some improvement in the area.
Overall async_hooks are *very* costly, especially with promises.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#181 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAmJPg_nTlHJe-Qr1xFoG-rEVv8Fq2rGks5uqsilgaJpZM4REc5B>
.
|
Tried it on Node 12.2 on Windows 10 U. Still costly.
|
@mcollina |
Is it possible to reduce performance impact of async_hooks in the future (in future Node.js versions)? Or the performance will be always so bad because of the nature of async_hooks and how they work? |
@alekbarszczewski it is mostly blocked on people doing the product work and suggesting concrete improvements. |
If anyone can provide pointers of where to start to try and fix this, and/or a tl;dr of why it's so slow, I can try to look into it. |
@rochdev how much time will you have to invest? It will be a relatively big ramp up. |
@rochdev my suggestion is to come to the next https://github.com/nodejs/diagnostics meeting. We are looking for a Champion to help with moving async hooks forward. As per the calendar the next meeting is scheduled for Oct 9 (next wed) and an issue will be opened in the repo next Monday with the meeting links etc. |
I definitely don't expect that it will be an easy task, but it's definitely an issue that is severely impacting APM vendors. I don't know if I'll be able to put a lot of time into it, at least short term, but I'd like to familiarize myself with the issue and how I'll try to join the next Diagnostics meeting. Thanks for the tip! |
I won't be able to make it to this week WG meeting. I'll make sure I stay available for the next one. |
I've already started a related discussions on Twitter earlier here, but Twitter doesn't scale for this kind of discussion, so I thought I should move it here, so it's easier to follow the discussion. For background: @jasnell approached me at NodeConfEU in November 2017, asking for help on the performance of
async_hooks
on the V8 side, especially when it comes to the Promise lifecycle hooks. And I've talked briefly with @thlorenz about it during the conference (although I have to admit that I was using the wrong terminology and thus kinda ruined the conversation, sorry). And we had chatted briefly about this as well with @trevnorris during one of the CTC-V8 calls.Since Promise based APIs are likely becoming a (big) thing for Node 10 and at the same time there are estimates (i.e. by @ofrobots) that by the end of next year every Node server in enterprise is going to run with
async_hooks
, we should start a discussion about this early one to stay ahead of the problem before it becomes a real problem for our users.In the last year @gsathya and @caitp already spent a lot of effort optimizing promises in V8 in general, so we're in a pretty good position wrt. baseline performance!
I'd like to use this forum to come to an agreement on what the concrete use cases are (at least estimate that), which aspects of the performance matter the most (i.e. promises created inside of C++ vs. in JS land), and what are useful benchmarks to drive the performance work in a meaningful way. I think we need both a set of micro-benchmarks to drive certain, in addition to real-world code that makes heavy use of promises, i.e. koa.js or fastify servers maybe? I feel that these benchmarks are most important, otherwise we might easily sink a lot of effort into work that doesn't help real-world use cases in the end. That's why I opened the issue on the benchmarking WG.
Comments and contributions are very welcome!
The text was updated successfully, but these errors were encountered: