Skip to content
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

AnonymousPipeStreams.Linux, Process.Unix Streams: perform async I/O by using Socket implementation #44647

Merged
merged 16 commits into from
Mar 17, 2021

Conversation

tmds
Copy link
Member

@tmds tmds commented Nov 13, 2020

This change is to see if there are functional, or performance changes when using read/write instead of recvmsg/sendmsg.

By using read/write we could look into leveraging the existing Socket (and SocketAsyncEngine) for performing async operations on pipe file descriptors, see #44329.

@adamsitnik @sebastienros can you benchmark this using the techempower benchmarks?

cc @stephentoub @wfurt

@ghost
Copy link

ghost commented Nov 13, 2020

Tagging subscribers to this area: @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

Issue Details
Description:

This change is to see if there are functional, or performance changes when using read/write instead of recvmsg/sendmsg.

By using read/write we could look into leveraging the existing Socket (and SocketAsyncEngine) for performing async operations on pipe file descriptors, see #44329.

@adamsitnik @sebastienros can you benchmark this using the techempower benchmarks?

cc @stephentoub @wfurt

Author: tmds
Assignees: -
Labels:

area-System.Net

Milestone: -

@tmds tmds changed the title [NO MERGE] experiment: use read/write for socket operations instead of recvmsg/sendmsg [NO MERGE] experiment: use read/write for socket operations instead of recv/send Nov 13, 2020
@stephentoub
Copy link
Member

Nice. Thanks for trying this. Did you notice any impact in local microbenchmarks?

@sebastienros
Copy link
Member

TE benchmarks are currently broken on net6.0. But I am trying hard to find why ...

@adamsitnik
Copy link
Member

cc @geoffkizer

@antonfirsov

This comment has been minimized.

@azure-pipelines

This comment has been minimized.

@tmds
Copy link
Member Author

tmds commented Nov 16, 2020

I worked on this further to the point where AnonymousPipeStream uses Socket under the hood.

I've added an bool IsPipe to SafeSocketHandle that tracks the handle is actually a pipe. I still need a way to make it non-public and be able to set it from System.IO.Pipes.

@tmds tmds changed the title [NO MERGE] experiment: use read/write for socket operations instead of recv/send [NO MERGE] experiment: use Socket for async I/O on pipe Nov 16, 2020
@antonfirsov
Copy link
Member

/azp run runtime-libraries-coreclr outerloop

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@geoffkizer
Copy link
Contributor

Why not create new operation types in SocketAsyncContext? If you do that, you shouldn't have to touch the existing send/recv paths at all.

@tmds
Copy link
Member Author

tmds commented Nov 16, 2020

Why not create new operation types in SocketAsyncContext? If you do that, you shouldn't have to touch the existing send/recv paths at all.

I think it will result in code duplication until we meet the SafeSocketHandle.IsPipe check at a 'higher level'.

@stephentoub
Copy link
Member

TE benchmarks are currently broken on net6.0. But I am trying hard to find why ...

@sebastienros, were you able to get these working again? Thanks!

@sebastienros
Copy link
Member

It's working again (since yesterday).

crank --config https://raw.githubusercontent.com/aspnet/Benchmarks/master/scenarios/platform.benchmarks.yml --scenario json --profile aspnet-citrine-lin --application.framework net6.0

In case I can't find the time to build the PR, to use a custom dll, add:

--application.options.outputFiles c:\release\*.dll

@adamsitnik
Copy link
Member

@tmds I've built your fork and run the JSON platform benchmarks in 4 configruations:

  • 00 without your changes, DOTNET_SYSTEM_NET_SOCKETS_INLINE_COMPLETIONS=0
  • 01 without your changes, DOTNET_SYSTEM_NET_SOCKETS_INLINE_COMPLETIONS=1
  • 10 with your changes, DOTNET_SYSTEM_NET_SOCKETS_INLINE_COMPLETIONS=0
  • 11 with your changes, DOTNET_SYSTEM_NET_SOCKETS_INLINE_COMPLETIONS=1

The results:

application
.NET Core SDK Version 6.0.100-alpha.1.20568.5
.NET Runtime Version 6.0.0-alpha.1.20577.3+17f51...
ASP.NET Core Version 6.0.0-alpha.1.20576.2+2c175...
load 00 10 01 11
CPU Usage (%) 74 74 77 77
Cores usage (%) 2,075 2,080 2,153 2,164
Working Set (MB) 48 48 48 48
Build Time (ms) 3,803 3,722 3,688 3,731
Start Time (ms) 0 0 0 0
Published Size (KB) 76,401 76,401 76,401 76,401
First Request (ms) 67 74 64 72
Requests/sec 1,187,853 1,189,332 1,235,426 1,242,720
Requests 17,936,371 17,957,897 18,654,912 18,764,486
Mean latency (ms) 0.67 0.66 0.42 0.41
Max latency (ms) 40.41 47.82 33.49 32.74
Bad responses 0 0 0 0
Socket errors 0 0 0 0
Read throughput (MB/s) 165.39 165.60 172.02 173.03
Latency 50th (ms) 0.39 0.39 0.40 0.40
Latency 75th (ms) 0.44 0.45 0.43 0.43
Latency 90th (ms) 0.54 0.53 0.46 0.46
Latency 99th (ms) 9.40 9.30 0.59 0.60

the trace files are available here

@stephentoub
Copy link
Member

The results

Thanks for checking, @adamsitnik. That's good, that means this has basically zero impact, which is as good as we could hope for.

The only number that jumps out at me is the First Request ms value. Is that difference just noise, or is that meaningful?

@tmds tmds changed the title [NO MERGE] experiment: use Socket for async I/O on pipe AnonymousPipeStreams.Linux: perform async I/O by using Socket implementation Dec 3, 2020
@tmds
Copy link
Member Author

tmds commented Dec 3, 2020

Thanks for running those benchmarks Adam.

Since the benchmarks show no regression, I've worked out the implementation further. These new changes won't impact the benchmarks.

I'm struggling to make the Socket creation for AnonymousPipeStreams internal, and not have it removed by the linker.

There are a couple of ways this could be implemented. I've avoided the work and risk of refactoring SocketAsyncEngine out of System.Net.Sockets, and kept the handle (which is likely passed between different processes) as a pipe.

@ghost
Copy link

ghost commented Dec 3, 2020

Tagging subscribers to this area: @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

Issue Details

This change is to see if there are functional, or performance changes when using read/write instead of recvmsg/sendmsg.

By using read/write we could look into leveraging the existing Socket (and SocketAsyncEngine) for performing async operations on pipe file descriptors, see #44329.

@adamsitnik @sebastienros can you benchmark this using the techempower benchmarks?

cc @stephentoub @wfurt

Author: tmds
Assignees: -
Labels:

area-System.Net.Sockets

Milestone: -

@davidfowl
Copy link
Member

Would it be better to move this polling into corelib and decouple it from sockets so other things can take advantage?

Copy link
Member

@stephentoub stephentoub left a comment

Choose a reason for hiding this comment

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

Looking good. Thanks for working on this. Are there any cancellation-related tests we can enable now or add for pipe streams on unix?

@stephentoub
Copy link
Member

@geoffkizer, can you take a look at the socket parts?

@tmds tmds changed the title AnonymousPipeStreams.Linux: perform async I/O by using Socket implementation AnonymousPipeStreams.Linux, Process.Unix Streams: perform async I/O by using Socket implementation Mar 10, 2021
@geoffkizer
Copy link
Contributor

I don't have any major concerns -- a couple small issues above.

@geoffkizer
Copy link
Contributor

My concerns have all been addressed -- @stephentoub is this ready to merge?

@stephentoub
Copy link
Member

/azp run

@azure-pipelines
Copy link

You have several pipelines (over 10) configured to build pull requests in this repository. Specify which pipelines you would like to run by using /azp run [pipelines] command. You can specify multiple pipelines using a comma separated list.

@stephentoub
Copy link
Member

/azp list

@stephentoub
Copy link
Member

/azp run runtime

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@stephentoub
Copy link
Member

Thanks, @tmds!

@stephentoub stephentoub merged commit 2606d82 into dotnet:main Mar 17, 2021
{
if (_isAsync)
{
return ReadAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult();
Copy link
Member

Choose a reason for hiding this comment

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

I know this was just copied from before; but why is this ReadAsync(...).GetAwaiter().GetResult() and Read(Span) is base.Read(buffer)?

Couldn't this also be

return base.Read(new Span(buffer, offset, count));

Or is the other one wrong?

Copy link
Member

Choose a reason for hiding this comment

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

@benaadams the Stream.Read(Span) method has an additional overhead of renting an array from the ArrayPool and copying the memory:

public virtual int Read(Span<byte> buffer)
{
byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
try
{
int numRead = Read(sharedBuffer, 0, buffer.Length);
if ((uint)numRead > (uint)buffer.Length)
{
throw new IOException(SR.IO_StreamTooLong);
}
new ReadOnlySpan<byte>(sharedBuffer, 0, numRead).CopyTo(buffer);
return numRead;
}
finally
{
ArrayPool<byte>.Shared.Return(sharedBuffer);
}
}

this is why we use Read(Array) here and in few other places like FileStream

Copy link
Member

Choose a reason for hiding this comment

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

Ah, and then it calls into the above overload using the array

@ghost ghost locked as resolved and limited conversation to collaborators Apr 16, 2021
@karelz karelz added this to the 6.0.0 milestone May 20, 2021
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.

None yet