6 ms·
I want to point out that in the thread here on HN (https://news.ycombinator.com/item?id=8631022 https://news.ycombinator.com/item?id=8631022) about that Netflix
by hardwaresofton 12y ago
I want to point out that in the thread here on HN (https://news.ycombinator.com/item?id=8631022 https://news.ycombinator.com/item?id=8631022) about that Netflix article, the top comment debunks the myth that the list-of-handlers was the issue slowing them down.
While creating a grammar from the combination of the regular expressions (which is why you can't just have a hash) is definitely a solution (quite possibly THE solution), that's definitely not an MVP-type of solution, and is pretty non-trivial.
The actual problem was them constantly adding handlers by accident.
One of the reasons I love and use HN so much (I give almost no other site so much pull on my personal opinion) is that smart (or very careful and well-read dumb people) leave well-thought-out comments like the one I mentioned.
- kevingadd 12y agoThis is all true. However, I can't take a stack seriously as an enterprise or production ready piece of software when it does something like iterate a list recursively in performance sensitive code. It's just bizarre. JS doesn't have tail call optimization and for loops are just fine. Being able to blow out the JS stack by having 'too many handlers' is terrifying to me, especially because JS runtimes have catastrophically poor support for handling and debugging stack overflows. The use of a single array for handlers that can contain multiple items also feels like an architectural red flag to me, but I can understand how that would happen since JS's standard library is so anemic and lacks useful data structures. What you really want is a set (or ordered set, if handler order is super important - it probably is here) I saw a number of entries in those flame charts that appeared to be V8 compilation internals, which is suspicious to me. But those are probably just debugger/symbol resolution issues, I hope, and not actual JIT recompilation constantly happening during route dispatch? (If v8 were compiling code constantly during dispatch, that would certainly explain multiple-ms pauses...)
- thedufer 12y ago> However, I can't take a stack seriously as an enterprise or production ready piece of software when it does something like iterate a list recursively in performance sensitive code. It's just bizarre. JS doesn't have tail call optimization and for loops are just fine. Can you explain how you would do asynchronous tasks in series without recursion? For loops successfully handle this when either the tasks are synchronous or can be run in parallel. Neither of those are true here.
- kevingadd 12y agoVirtually all real-world stacks I know of use a work queue where each element in the list is queued up once it's ready. You don't need on-stack recursion or unbounded memory to track the execution states. It's pretty trivial to implement this given a promises API and any sort of event queue, but you could also just do it with callbacks. So in the browser you'd literally just use setTimeout. One reason this is so common is that it's trivial to end up blowing out the stack with recursive IO callbacks, because sometimes all your read()s complete instantly because all the data's in cache. Oops!
- hardwaresofton 12y agoHow would you handle their problem? Maybe this can turn into a chance to write some code (I'm willing to) to improve express?
- kevingadd 12y agoSee this post for an explanation: https://news.ycombinator.com/item?id=8632604 https://news.ycombinator.com/item?id=8632604 The 'push ready callbacks onto a queue' model is really common and relatively straightforward to optimize. I've used it in a variety of applications and it can provide very good throughput in both single and multiple threaded scenarios.
- hardwaresofton 12y agoI did read that comment of yours, to make it more concrete -- 1. Create some sort of handler execution queue 2. Push first handler onto queue 3. When a handler runs, which may translate into the firing of another handler/ translation of the route, push a "handle callback" (for lack of a better term) onto the queue 4. Return to #3 and continue forever (which is a possibility) or run out of handlers at some point (be done)
- kevingadd 12y agoYes. Most robust implementations have a limit on how long they will pump the queue instead of continuing forever - in most of my applications I cap it at somewhere around 1-10ms. You'd want a shorter limit for realtime stuff like games, a higher limit for high-throughput scenarios like servers. When the limit is reached I yield to the event loop and then continue running handlers from the queue after that.
- _greim_ 12y ago> JS doesn't have tail call optimization and for loops are just fine. Not personally familiar with Express's internals, but if this is async iteration nothing's pushing onto the call stack in the first place. Unless you're using generators, but in that case you can just use a for loop.
- kevingadd 12y agoThe deeply nested stack in the flame chart suggests that it is recursing, and the netflix article said it's recursing...
- rudolf0 12y agoYep, I think both parties are sort of in the wrong. It's silly how Express currently handles routes, and it's silly of the Netflix engineer to suggest that they should use maps instead.
- mbesto 12y ago> However, I can't take a stack seriously as an enterprise or production ready piece of software when it does something like iterate a list recursively in performance sensitive code. It's just bizarre. Which is why open source is so beautiful -- someone who is "more serious" can come along, download the source, rewrite that functionality and submit a fork request.