Live data from Hacker News

A better streams API is possible for JavaScript

blog.cloudflare.com

121–130 of 167 posts

Re: A better streams API is possible for JavaScript

#121
There are many use cases where having a value stream is very useful. I do agree having a separate simpler byte only stream would make sense though. I think the current capabilities of web streams should be kept and an IOStream could be added for optimizing byte streams.

Ideally splitting out the use cases would allow both implementations to be simpler, but that ship has probably sailed.

Re: A better streams API is possible for JavaScript

#122
I ran into a performance issues a few months ago where native streams were behaving terribly, and it seemed to be due to bad back-pressure implementation.

I tried several implementations, tweaked settings, but ultimately couldn't get around it. In some cases I had bizarre drops in activity when the consumer was below capacity.

It could have been related to the other issue they mention, which is the cost of using promises. My streams were initiating HEAPS of promises. The cost is immense when you're operating on a ton of data.

Eventually I had to implement some complex logic to accomplish batching to reduce the number of promises, then figure out some clever concurrency strategies to manage backpressure more manually. It worked well.

Once I was happy with what I had, I ported it from Deno to Go and the result was so stunningly different. The performance improvement was several orders of magnitude.

I also built my custom/native solution using the Effect library, and although some people claim it's inefficient and slow, it out-performed mine by something like 15% off the shelf, with no fine-tuning or clever ideas. I wished I'd used it from the start.

The difference is likely in that it uses a fiber-based model rather than promises at the execution layer, but I'm not sure.

Re: A better streams API is possible for JavaScript

#123
https://socketcluster.io/ has had such stream implementation and backpressure management since at least 2019.

Here's the WritableConsumableStream module:

https://github.com/SocketCluster/writable-consumable-stream

SocketCluster solves the problem of maintaining message order with async processing.

This feature is even more useful now with LLMs as you can process data live, transform streams with AI with no risk of mangling the message order.

I may have been the first person to use a for-await-of loop in this way with backpressure. At least on an open source project.

Re: A better streams API is possible for JavaScript

#125

Earlier quoted context omitted.

How did you come up with 90? Can you shed any might on the difference between the cost of promise resolution and the cost of await? Is there any cost component with how deep in the call stack you are when an await happens?

Essentially for loop of 10k iterations comparing `fn()` versus `await fn()` fed into a microbenchmark tool, with some fiddling to detect if elimination was happening or ordering was changing things. I was bumping into PRs trying to eliminate awaits in long loops and thinking surely the overhead can’t be so high to warrant doing this, especially after node ~16. I was wrong.

Clarifying:

‘fn()’ is the exact same function call in both cases. Only the benchmark itself was async versus sync.

Because the case under test is what’s the cost of making an async api when 90% of the calls are sync? And the answer is a lot higher than I thought.

Re: A better streams API is possible for JavaScript

#126
post #124

I've only used them in dotnet, I would be very interested to read any strong opinions about the use of Streams both in practice and as an abstract point in API design.

Streams are how modern operating systems work, most commonly to transfer audio, video, file system, and network data from hardware to channels available for applications. So a common scenario is to stream data from a file and pipe it to a network interface for transfer to other computers or to a web browser.

Re: A better streams API is possible for JavaScript

#128

Earlier quoted context omitted.

Your idea is flatten the UInt8Array into the stream. While I understand the logic, that's a terrible idea. * The overhead is massive . Now every 1KiB turns into 1024 objects. And terrible locality. * Raw byte APIs...network, fs, etc fundamentally operate on byte arrays anyway. In the most respectful way possible...this idea would only be appealing to someone who's not used to optimizing systems for efficiency.

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…

The allocation of each object still has overhead though, even if they all live side-by-side. You get memory overhead for each value. A Uint8Array is tailor-made for an array of bytes and there’s a constant overhead. Plus the garbage collector doesn’t even have to peer inside a Uint8Array instance.

Re: A better streams API is possible for JavaScript

#129
As a maintainer on the Ky team, I give a big thumbs up to this proposal.

We have run into many problems with web streams over the years and solving them has always proven to be hairy, including the unbounded memory growth from response.clone().

The Deno team implemented a stream API inspired by Go, which I was happy with, until they ultimately acquiesced to web streams.

This proposal shares some of those principles as well.

Re: A better streams API is possible for JavaScript

#130
post #99
post #50

Earlier quoted context omitted.

True. But it’s also true that trying to shoehorn every use case into TCP streams is counter productive. A stream API can layer over UDP as well (reading in order of arrival with packet level framing), but such a stream would a bit weird and incompatible with many stream consumers (e.g. [de]compression). A UDP API is simpler and more naturally event (packet) oriented. The concepts don’t mix well. Still, it would be ni…

The browser does have a UDP data stream available for applications to send arbitrary bytes over UDP; it's part of WebRTC.

While Web RTC is built on UDP, it does not allow sending arbitrary UDP. It's DTLS, perhaps encapsulating SCTP, and DCEP.
Post reply on HN