-
Notifications
You must be signed in to change notification settings - Fork 10.4k
If not waiting for data, allocate memory before checking anything #22225
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
Conversation
{ | ||
// If not waiting for data allocate another buffer immediately after flushing, | ||
// to try and aquire some before the reader takes the lock. | ||
buffer = input.GetMemory(MinAllocBufferSize); |
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.
Given that the flushTask should always be completed in the benchmark we're looking at, why do you expect calling GetMemory() here instead of the top of the loop to be substantially different? Is it so expensive to retrieve the FlushResult and check few more bools that it's likely to change the timing of GetMemory() relative to another thread waking up?
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 mention in dotnet/runtime#36992 (comment) that its basically running a data race; in combination with PR dotnet/runtime#36992 it just needs to set the IsWritingActive
flag near the start of GetMemory()
before the Reader takes the lock to avoid the lock contention.
I'm not sure it will work and if not then PipeWriter would need a FlushAsync(bool moreData = false)
overload; where it doesn't switch out of writing when moreData
is true
, akin to the linux send(2)
flag MSG_MORE
, the Windows RioSend
RIO_MSG_DEFER
flag; or WSASend
MSG_PARTIAL
flag; which would allow it to either keep using the existing buffer
or request a replacement larger one without having the data race.
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.
FlushAsync(bool moreData = false)
would be better as Memory.Slice
is cheaper than GetMemory()
😉
@@ -209,7 +216,18 @@ private async Task ProcessReceives() | |||
|
|||
input.Advance(bytesReceived); | |||
|
|||
var flushTask = input.FlushAsync(); | |||
var flushTask = input.FlushAsync(isMoreData: !_waitForData); |
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.
@benaadams @davidfowl Any chance we're getting the isMoreData:
API in .NET 5?
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.
No; too soon
{ | ||
if (buffer.Length - bytesReceived >= MinAllocBufferSize) | ||
{ | ||
buffer = buffer.Slice(bytesReceived); |
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 skipping of the call to GetMemory() doesn't even look like it should be allowed by the PipeWriter. Is this critical for perf?
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.
Its what the api would allow
@benaadams I'm closing this dotnet/runtime#37472 has also been closed. Hopefully we can optimize this more in .NET 6. |
To reduce contention between reader and writer on the MemoryPool (as no-delay in the no wait scenario) seen in dotnet/runtime#36956 (comment)
Its a race to see if it can get it before the Flush is picked up; but the Flush invalidatees outstanding Memory, so we can't do it before the Flush :-/