Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Minor perf improvements to Sockets #21051

Merged
merged 7 commits into from
Jun 14, 2017
Merged

Conversation

stephentoub
Copy link
Member

Shaves a few percent off various socket operations. It's close to noise, but appears to be statistically significant and amount to around 2-3% improvement in microbenchmarks that repeatedly receive/send (in which case receives complete asynchronously) and send/receive (in which case most if not all operations complete synchronously).

(At this point most of the overhead associated with sockets (at least in the Windows build) is either in the P/Invokes to native or in peanut butter spread across lots of functions with very low exclusive amounts per function. There's likely a decent amount more to be done on Linux, where we've done less profiling and where we know there are improvements to be made around asynchronous completion and dispatch.)

cc: @geoffkizer, @CIPop, @davidsh, @vancem

Shaves a small but measurable amount off each StartConfiguring call.
It's not 100% the same logic as before; previously, if an operation was in progress while Dispose was called, and then another operation was issued while that previous operation was still in progress, this would throw an OperationDisposedException, whereas now it'll throw an InvalidOperationException until the previous operation completes, then it'll throw an OperationDisposedException.  But a) there are race conditions here, so code doing this needs to be able to handle both exceptions for the same failure, and b) the previous logic was inconsistent with the exact same kind of thing in StartConfiguring, for no good reason. This brings them in sink, and shares the same code, also reducing the size of StartOperationCommon.
We only need to update the socket if it's changed.
Move most of the implementation to a "slow" helper
- Factor out duplicated blobs for logging into a single location
- Remove cases that are unnecessary after logging change
- Avoid unnecessary status check / branch for operations that don't change it (only accept/connect do)
@@ -33,52 +33,56 @@ public ThreadPoolBoundHandle IOCPBoundHandle
// Binds the Socket Win32 Handle to the ThreadPool's CompletionPort.
public ThreadPoolBoundHandle GetOrAllocateThreadPoolBoundHandle(bool trySkipCompletionPortOnSuccess)
{
// Check to see if the socket native _handle is already
// bound to the ThreadPool's completion port.
if (_released || _iocpBoundHandle == null)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we simplify this to a single test? Is _iocpBoundHandle always null when _released == true? Seems like it should be...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe with other refactorings or changes, but at present it doesn't look like it... _iocpBoundHandle starts out as null and is then lazily initialized to non-null, after which point it's never set back to null. From the comments in the code, it looks like this is done to support freeing a NativeOverlapped after disposal.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense.

Another idea: is there any harm in just returning the existing iocp-bound handle if _released is true and _iocpBoundHandle is not null?

Don't know if any of this matters in practice, so don't block on this...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean changing the condition from:

if (_released || _iocpBoundHandle == null) 

to instead just be:

if (_iocpBoundHandle == null) 

such that we don't throw the ObjectDisposedException from here but instead presumably have it throw some kind of exception when the handle is actually used?

Sounds reasonable, but I've not tried it. Not sure if we have tests for that, either. But it'd be worth checking on subsequently.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that's what I was thinking.

@geoffkizer
Copy link

Generally looks great; one small comment above.

@stephentoub stephentoub merged commit 7854da4 into dotnet:master Jun 14, 2017
@stephentoub stephentoub deleted the socket_perf branch June 14, 2017 22:05
@karelz karelz modified the milestone: 2.1.0 Jun 15, 2017
stephentoub pushed a commit that referenced this pull request Nov 30, 2018
The jit is fairly tolerant of byref/native int mismatches for inline arguments
and return values. And some of the new unsafe helpers do this kind of
reinterpretation across call boundaries as well. This sometimes leads the jit
to lose track of byrefs.

A general fix for this is likely somewhat involved. For now we simply detect if
we're about to lose track of a byref when substituting a particular kind of
return expression, and retype the expression to a byref.

Fixes #21051.

Signed-off-by: dotnet-bot <dotnet-bot@microsoft.com>
jlennox pushed a commit to jlennox/corefx that referenced this pull request Dec 16, 2018
The jit is fairly tolerant of byref/native int mismatches for inline arguments
and return values. And some of the new unsafe helpers do this kind of
reinterpretation across call boundaries as well. This sometimes leads the jit
to lose track of byrefs.

A general fix for this is likely somewhat involved. For now we simply detect if
we're about to lose track of a byref when substituting a particular kind of
return expression, and retype the expression to a byref.

Fixes dotnet#21051.

Signed-off-by: dotnet-bot <dotnet-bot@microsoft.com>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants