padenot / ringbuf.js

Wait-free thread-safe single-consumer single-producer ring buffer using SharedArrayBuffer
https://ringbuf-js.netlify.app/
Mozilla Public License 2.0
201 stars 18 forks source link

Atomics.wait support #13

Closed asilvas closed 2 years ago

asilvas commented 2 years ago

Howdy, cool project! Curious if you have any plans to support signaling via Atomics.wait to avoid the need for hard polling?

padenot commented 2 years ago

Atomics.wait isn't generally real-time safe, so not really. What's the use-case?

Polling is generally a lot more efficient than other techniques, because there's already something that is called repeatedly (render loop, decoding loop, AudioWorkletProcessor.process, etc.), using a system call to wake up a sleeping thread is a waste of time.

asilvas commented 2 years ago

This is for nodejs where a dedicated thread is waiting for more work off the ring buffer. Using hard polling is very CPU intensive.

padenot commented 2 years ago

It sounds to me that you're not looking for a wait-free queue, right? Clearly you want to wait, and you're not in a real-time scenario? Is postMessage or waiting on another bit of memory with wait/notify or something like that not good enough? Wrapping this SPSC ring buffer in something else that provides the wait/notify might be the right design here.

asilvas commented 2 years ago

This is a real-time scenario. Performance implementations of ring buffers don't typically rely on hard polling as it's very wasteful on resources. I can look into exposing in a different package if it's not something you're interested in supporting.

padenot commented 2 years ago

Well, a real-time safe data structure can't use wait or notify either, as this ends up potentially doing system calls. wait isn't available in, say AudioWorkletGlobalScope anyway, which is the only way to get a real-time thread on the web, and notify takes a lock (spidermonkey, v8), so it cannot be used in wait-free code.

What kind of real-time are we talking here? This package has been originally written for hard-real-time, low latency audio processing scenario on the Web, where one thread is running in a different scheduling class, and other threads either provide or consume data from/to it, and the real-time thread can never block. Those types of ring buffer usually never have notify/wait primitives (here are a few I know of, mostly in rust, C++ or C, in use in various projects already: https://docs.rs/rtrb/latest/rtrb/, https://libcinder.org/docs/classcinder_1_1audio_1_1dsp_1_1_ring_buffer_t.html, https://www.boost.org/doc/libs/1_63_0/doc/html/boost/lockfree/spsc_queue.html, https://github.com/jackaudio/jack2/blob/852ba2b8ebc944865e75d8eb5d4550d408772f3b/common/jack/ringbuffer.h, https://searchfox.org/mozilla-central/source/mfbt/SPSCQueue.h, http://files.portaudio.com/docs/v19-doxydocs-dev/pa__ringbuffer_8c.html).

But real-time is a spectrum, other use-cases might be softer than that. It would be very useful to have something that sits on top of this queue (it can be made efficient since the memory is passed as an argument, cache wise), and provide nicer synchronization, e.g. with notify/wait (when available), or some sort of futex. But it's probably not possible to write a good futex at the moment at least on the web, lack of synchronous sleep or access to the proper intrinsic, or an equivalent of sched_yield. Maybe node has those, do you know?

asilvas commented 2 years ago

I understand now this package was designed for web so I'll move on. My background on this topic is with POSIX so I'll investigate the low levels more in node/v8 to determine the feasibility/tradeoffs of leveraging notify/wait. Long story short, I have many threads processing commands and I don't want those threads each consuming 100% of each CPU core. My current implementation leverages postMessage (which works well) but even with efficient v8 serialization there is still considerable overhead for real-time software, thus why I'm looking into leveraging ring buffers for many-to-many thread communication. Thanks for taking the time!

padenot commented 2 years ago

I wonder if it's possible to port one of the moderne work-stealing queue algorithms in the current state of nodejs' ecosystem, that would probably be the best you can do (not knowing too much about the problem though).