Add Lock-free Circular buffer#52
Conversation
|
@rnburn would you help to clarify where would this buffer get used? I guess in the batch exporter ( |
|
When you finish a span, it would get added into a version of this buffer |
My understanding is that we don't put span into a queue by default. Here goes the spec. |
|
Here goes a Python example on how synchronous exporter is used. |
|
You will definitely want some kind of buffering for most use cases.
If the span is processed synchronously without buffering, that will mean possibly blocking the thread that finished the span for an indefinite amount of time. That would be unacceptable for many applications. But I see buffering in the Python code here. |
Depends on the scenario. For people who use LTTng or ETW, they will be shocked if you put in-proc buffer.
Yes, and this is specific for |
| * Atomically swap the pointer with another. | ||
| * @param ptr the pointer to swap with | ||
| */ | ||
| void Swap(std::unique_ptr<T> &ptr) noexcept { ptr.reset(ptr_.exchange(ptr.release())); } |
There was a problem hiding this comment.
Would s/ptr/other/ improve readability?
|
|
||
| bool Add(std::unique_ptr<T> &&element) noexcept | ||
| { | ||
| std::lock_guard<std::mutex> lock_gaurd{mutex_}; |
|
Ok, but this PR isn't about mandating a buffer anywhere. It only provides a lock-free buffer for the scenarios that do buffering. |
Co-Authored-By: Reiley Yang <reyang@microsoft.com>
…try-cpp into circular-buffer
… into circular-buffer
…try-cpp into circular-buffer
|
@g-easy @pyohannes - could you guys take a look? thanks. |
pyohannes
left a comment
There was a problem hiding this comment.
Looks good to me. I have one small questions that's not critical.
| template <class Callback> | ||
| void Consume(size_t n, Callback callback) noexcept | ||
| { | ||
| assert(n <= static_cast<size_t>(head_ - tail_)); |
There was a problem hiding this comment.
It's intended that users of CircularBuffer have to check the buffer size each time before calling Consume? Instead of n being the maximum of elements consumed?
There was a problem hiding this comment.
You may not always want to consume the maximum number of elements. For example, you might first call Peek to get a range of elements in the buffer, then call Consume to remove those elements if they were processed successfully. Passing the size is necessary since it's a concurrently multi-producer, single-consumer buffer and elements could have been added between the calls to Peek and Consume.
Adds a lock-free buffer than can be used for spans to ensure that a thread with instrumentation in a heavily concurrent environment doesn't get blocked on a mutex.
Includes a benchmark that compares performance to a baseline circular buffer with a mutex. Here are the results: