Hacker News new | ask | show | jobs
by bigdubs 741 days ago
Many ring-buffer implementations grow the backing storage array transparently on enqueue but do so in place, discarding the old arrays; what's the advantage of keeping the previous arrays? Naively I'd say it would reduce GC churn because you wouldn't have to free the old arrays, but I'm curious what the impact of that is in benchmarks.

Separately; the simulator is cool and very helpful!

2 comments

If you discard the old array (and allocate a bigger one before), you must also copy all enqueued material.

Also this one enqueue will be mega expensive - a clear "fat tail" in the latency histogram.

In MultiArrayQueue you keep all already enqueued material in-place, "just" allocate the bigger array, register the new diversion, enqueue the one new element - and done.

Thanks

why not free previous smaller chunk after reader finished reading from it?

for me it would be better to allocate new buffer but allow reading from old one until it contains data and after that dealocate it and keep only new one in use

You might run into ABA, though that isn't an issue for managed languages.
Yeah, leaving small old buffers behind seems like a major no-no to me. It could be useful if you think you'll shrink back down, but it feels like cache locality suffering and iteration/tracking penalties strongly incentive getting rid of the old buffer asap.

One other thing I want to shout out, I saw what I thought was a really neat multicast ring buffer the other day where the author has an atomic for each element, rather than the typical reader/writer atomics. The promise was having much less contention on any given atomic, in most cases. https://github.com/rezabrizi/SPMC-Queue https://news.ycombinator.com/item?id=40410172

Removing anything in non-blocking structures is problematic, see e.g. the referenced lecture of Professor Scott.

You never know how many concurrent threads still "are" on the place you wish to remove.

You would have to deal with stuff like hazard pointers, limbo lists and the like.

Better to keep the small arrays there.

Isn't this only issue when you allow referencing data in queue?

If queue only allows to copy out data you can increase reader pointer after data were copied to different buffer, therefore nothing can be at the place we are removing

With ConcurrentMultiArrayQueue, there can be N threads INSIDE of the program code of the Queue, running or preempted (for a not predictable time) and you cannot control it.
When I wrote it [0], the goal was semi-bounded [1] latency.

[0] It needs a refactor, but here's some version of the idea. https://github.com/hmusgrave/zcirc

[1] You're not _really_ bounded since you have blocking calls to the underlying allocator (usually somewhere close to the OS, and even that can do more work than you might expect when you ask for a contiguous array), but it's still much cheaper than a bulk copy.