-
Notifications
You must be signed in to change notification settings - Fork 765
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor exporter - step 4 #1085
Conversation
Codecov Report
@@ Coverage Diff @@
## master #1085 +/- ##
==========================================
- Coverage 76.59% 76.17% -0.43%
==========================================
Files 224 225 +1
Lines 6165 6199 +34
==========================================
Hits 4722 4722
- Misses 1443 1477 +34
|
|
||
if (this.SwapIfNull(index, value)) | ||
{ | ||
this.head = headSnapshot + 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know it's a really really big number, but in theory it could wrap around to negative at some point which would throw overflow exception I think? If we switched to ulong head/tail and did unchecked (this.head = headSnapshot + 1)
I think the wrap would be fine given the % this.capacity
in the algorithm?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it is impractical to hit that number? Adding the wrapping support requires more than unchecked
, it will touch other part of the lock-free algorithm and put additional requirements on memory barriers. So I would prefer to keep it as-is in favor of perf vs. mathematical correctness.
|
||
count = Math.Min(count, this.Count); | ||
|
||
for (int i = 0; i < count; i++) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you want to provide an additional check with a boolean and an Interlock to prevent 2 threads from accidently entering if the single reader pattern is accidently broken.
|
||
if (this.SwapIfNull(index, value)) | ||
{ | ||
if (Interlocked.CompareExchange(ref this.head, headSnapshot + 1, headSnapshot) == headSnapshot) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is headSnapshot
validation needed, or Interloaded.Add
can work here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's is required, otherwise it'll break the 🧙♀️.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, yes, it is required because the item pointed by headSnapshot
maybe not in the range of [this.tail, this.head).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
As the name implied it is a circular buffer, should it |
throw new ArgumentNullException(nameof(value)); | ||
} | ||
|
||
while (true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Retry here introduces uncertainty of running time of TryAdd
, perhaps return false instead of retry in the while(true)
loop, or limit retry count?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The intention of this function is to return false only when queue is full.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ThomsonTan you've actually worried me! 😅 Here goes a PR #1088 to address it.
private long head = 0; | ||
private long tail = 0; | ||
|
||
public CircularBuffer(int capacity) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Restrict capacity to be power of 2, so the modulo operation %
could replaced with a simple bitwise and?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How much (X%) perf improvement would you expect from modulo?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
div
could take tens times of CPU cycles than bitwise arithmetic instruction, but there is also trade off taking more memory (for ceiling(2**size)
). This could be a low priority optimization and needs data to prove.
{ | ||
if (count <= 0) | ||
{ | ||
yield break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the queue is empty, wait for notification of new items instead of polling?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would make it a normal lock based queue instead of lock-free.
This is a follow up PR of #1078.
Changes
For significant contributions please make sure you have completed the following items: