Live data from Hacker News

A better streams API is possible for JavaScript

blog.cloudflare.com

141–150 of 167 posts

Re: A better streams API is possible for JavaScript

#141

Earlier quoted context omitted.

JS engines actually are optimized to make that usage pattern fast. Small, short-lived objects with known key ordering (monomorphism) are not a major cost in JS because the GC design is generational. The smallest, youngest generation of objects can be quickly collected with an incremental GC because the perf assumption is that most of the items in the youngest generation will be garbage. This allows collection to be o…

Brother, you are talking about one object for every byte . That is a madness. And often for no reason...you're copying or arranging bytes in lists anyway.

That's just how string iterators work in Javascript: one object for every byte. For now it's fast enough: https://v8.dev/blog/trash-talk. I'd put the GC overhead at around 10-15%, even with 20+ objects per byte when you add up all the stages in a real text processing pipeline. It's that cheap because the objects all have short lifespans and so they spend all their lives in the "tiny short-lived objects" memory pool which is super easy to incrementally GC.

In the future it should be entirely possible for the engines to optimize even more aggressively too: they should be to skip making the object if the producer of values is a generator function and the consumer is a for loop.

Re: A better streams API is possible for JavaScript

#142

Earlier quoted context omitted.

Interesting, thanks for the info, I'll do some reading on what you're saying. I agree, you're right about JS having issues with hiccups in the UI due to scheduling on a single process thread. Makes a lot of sense, cool that the garbage collector can run independently of the call stack and function scheduler.

OP doesn’t know what he’s talking about. Creating an object per byte is insane to do if you care about performance. It’ll be fine if you do 1000 objects once or this isn’t particularly performance sensitive. That’s fine. But the GC running concurrently doesn’t change anything about that, not to mention that he’s wrong and the scavenger phase for the young generation (which is typically where you find byte arrays bein…

Even if you stop the world while you sweep the infant generation, the whole point of the infant generation is that it's tiny. Most of the memory in use is going to be in the other generations and isn't going to be swept at all: the churn will be limited to the infant generation. That's why in real usage the GC overhead is I would say around 15% (and why the collections are spaced regularly and quick enough to not be noticeable).

Re: A better streams API is possible for JavaScript

#143
Regarding the benchmarks, "Async iteration (8KB × 1000): ~530 GB/s vs ~35 GB/s": how do you achieve 530 GB/s throughput on an M1 Pro which has a 200GB/s memory bandwidth? The "~275 GB/s" figure for chained transforms has the same problem.

I suspect the benchmarks, if not most of this project, suffer from poor quality control on vibecoded implementations.

Re: A better streams API is possible for JavaScript

#144
post #37
post #22

Earlier quoted context omitted.

What was it specifically about the style that stood out as incongruous, or that hindered comprehension? What was it that made you stumble and start paying close attention to the style rather than to the message? I am looking at the two examples, and I can't see anything wrong with them, especially in the context of the article. They both employ the same rhetorical technique of antithesis, a juxtaposition of contrasti…

The problem is less with the style itself and more that it's strongly associated with low-effort content which is going to waste the readers time. It would be nice to be able to give everything the benefit of the doubt, but humans have finite time and LLMs have infinite capacity for producing trite or inaccurate drivel, so readers end up reflexively using LLM tells as a litmus test for (lack of) quality in order to c…

I find it more amusing that the benchmarks claim 530 GB/s throughput on an M1 Pro which has a 200GB/s memory bandwidth. The 275 GB/s figure for chained transforms has the same problem.

I suspect the benchmarks, if not most of this project, was completely vibecoded. There are a number of code smells, including links to deleted files, such as https://github.com/jasnell/new-streams/blob/ddc8f8d8dda31b4b... an inexistent REFACTOR-TODO.md

The presence of COMPLETENESS-ANALYSIS.md (https://github.com/jasnell/new-streams/blob/main/COMPLETENES...) isn't reassuring either, as it suggests the "author" of this proposal doesn't sufficiently understand the completeness of his own "work."

Re: A better streams API is possible for JavaScript

#145

Earlier quoted context omitted.

I'm fairly sure it's not Promises that are actually the heavy part but the `await` keyword as used in the `for await` loop. That's because await tries to preserve the call stack for debugging, making it a relatively high-level expensive construct from a perf perspective where a promise is a relatively low-level cheap one. So if you're going to flatten everything into one stream then you can't have a for loop implemen…

Async call stacks is an optional feature when the devtools is open. There shouldn't be overhead from await like that?

It's awfully hard to know, and I am not myself sure.

Re: A better streams API is possible for JavaScript

#146
post #5

It's a real shame that BYOB (bring your own buffer) reads are so complex and such a pain in the neck because for large reads they make a huge difference in terms of GC traffic (for allocating temporary buffers) and CPU time (for the copies). In an ideal world you could just ask the host to stream 100MB of stuff into a byte array or slice of the wasm heap. Alas.

[flagged]

Re: A better streams API is possible for JavaScript

#147
I don’t know how ReadableStream.tee() got specified to backpressure when the faster branch is not consumed, since this is the opposite of what nodejs does when multiple Writables attached via Readable.pipe() and also the opposite of what the requirements document (https://github.com/whatwg/streams/blob/e9355ce79925947e8eb49...) says: “letting the speed of the slowest output determine the speed of the tee”.

I like the idea of the more ergonomic, faster api in new-stream with no buffering except at Stream.push(). NodeJS and web streams put infinitely expandable queues at every ReadableStream and WritableStream so that you can synchronously res.write(chunk) as much as you want with abandon. This API basically forces you to use generators that yield instead of synchronously writing chunks.

Re: A better streams API is possible for JavaScript

#148

Earlier quoted context omitted.

OP doesn’t know what he’s talking about. Creating an object per byte is insane to do if you care about performance. It’ll be fine if you do 1000 objects once or this isn’t particularly performance sensitive. That’s fine. But the GC running concurrently doesn’t change anything about that, not to mention that he’s wrong and the scavenger phase for the young generation (which is typically where you find byte arrays bein…

Even if you stop the world while you sweep the infant generation, the whole point of the infant generation is that it's tiny . Most of the memory in use is going to be in the other generations and isn't going to be swept at all: the churn will be limited to the infant generation. That's why in real usage the GC overhead is I would say around 15% (and why the collections are spaced regularly and quick enough to not be…

I've been long on JS but never heard things like this, could you please prove it by any means or at least give a valid proof to the _around 15%_ statement? Also by saying _quick enough to not be noticeable_, what's the situation you are referring too? I thought the GC overhead will stack until it eventually affects the UI responsiveness when handling continues IO or rendering loads, as recently I have done some perf stuff for such cases and optimizing count of objects did make things better and the console definitely showed some GC improvements, you make me nerve to go back and check again.

Re: A better streams API is possible for JavaScript

#149
post #9
post #7

> The problems aren't bugs; they're consequences of design decisions that may have made sense a decade ago, but don't align with how JavaScript developers write code today. > I'm not here to disparage the work that came before — I'm here to start a conversation about what can potentially come next. Terrible LLM-slop style. Is Mr Snell letting an LLM write the article for him or has he just appropriated the style?

You’ve got it backwards: LLMs were trained on human writing and appropriated our style.

They converge...

Re: A better streams API is possible for JavaScript

#150
post #79

Earlier quoted context omitted.

I did a microbenchmark recently and found that on node 24, awaiting a sync function is about 90 times slower than just calling it. If the function is trivial, which can often be the case. If you go back a few versions, that number goes up to around 105x. I don’t recall now if I tested back to 14. There was an optimization to async handling in 16 that I recall breaking a few tests that depended on nextTick() behavior…

> I did a microbenchmark recently and found that on node 24, awaiting a sync function is about 90 times slower than just calling it. If the function is trivial, which can often be the case. I dabble in JS and… what?! Any idea why?

I assume the reason is that `await` de-schedules the current microtask. In fact, even if you immediately return from an `await`, the de-scheduling can introduce behavior that otherwise would be absent without `await`. For this reason, code optimizers (like the Google Closure Compiler) treat `await` as a side-effect and do not optimize it out.
Post reply on HN