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

Udp Socket.ReceiveFrom a lot of allocations for IPAdress #30196

Closed
kripergvg opened this issue Jul 9, 2019 · 5 comments
Closed

Udp Socket.ReceiveFrom a lot of allocations for IPAdress #30196

kripergvg opened this issue Jul 9, 2019 · 5 comments

Comments

@kripergvg
Copy link

At production server, I have a simple code

mSocket.ReceiveFrom(mBuffer, SocketFlags.None, ref ep);

I call this code every millisecond. Recently I found out that almost 40% of allocations from my application are from this code. And all of the allocations are only 3 primitives: SocketAddress, IPEndPoint, IPAdress. It seems wasteful. Maybe I do something wrong? Can I read data from the UDP socket without this allocations? Can I have managed UDP transport with zero allocations?
Simple reproduction https://github.com/kripergvg/UdpAllocations
dotmemory after 4 minutes
image

@scalablecory
Copy link
Contributor

ReceiveFrom variants have a surprisingly large number of allocations right now. This is likely to have a significant impact on QUIC. Seems worth a look at what we can do to improve things.

@scalablecory
Copy link
Contributor

What do you think of adding some APIs to use SocketAddress directly:

class Socket
{
    public int SendTo(ReadOnlySpan<byte> buffer, SocketFlags socketFlags, SocketAddress remoteAddress);
    public ValueTask<int> SendToAsync(ReadOnlyMemory<byte> buffer, SocketFlags socketFlags, SocketAddress remoteAddress);

    public int ReceiveFrom(Span<byte> buffer, SocketFlags socketFlags, SocketAddress remoteAddress);
    public ValueTask<int> ReceiveFromAsync(Memory<byte> buffer, SocketFlags socketFlags, SocketAddress remoteAddress);
}

As well as some changes to conveniently access the result:

class SocketAddress
{
    public ReadOnlyMemory<byte> IPAddress { get; }
    public int Port { get; }
}

@scalablecory
Copy link
Contributor

scalablecory commented Aug 26, 2019

I wrote a quick proof of concept to test this.

Before:
before

After:
after

For a loop of 200,000 send/recv between 10 clients and 1 server, we remove about 2,200,000 allocations, and go from 12 GCs down to 0.

@kripergvg
Copy link
Author

What do you think of adding some APIs to use SocketAddress directly:

class Socket
{
    public int SendTo(ReadOnlySpan<byte> buffer, SocketFlags socketFlags, SocketAddress remoteAddress);
    public ValueTask<int> SendToAsync(ReadOnlyMemory<byte> buffer, SocketFlags socketFlags, SocketAddress remoteAddress);

    public int ReceiveFrom(Span<byte> buffer, SocketFlags socketFlags, SocketAddress remoteAddress);
    public ValueTask<int> ReceiveFromAsync(Memory<byte> buffer, SocketFlags socketFlags, SocketAddress remoteAddress);
}

As well as some changes to conveniently access the result:

class SocketAddress
{
    public ReadOnlyMemory<byte> IPAddress { get; }
    public int Port { get; }
}

Looking amazing. It would solve my problem.

@karelz
Copy link
Member

karelz commented Sep 9, 2019

Duplicate of dotnet/corefx#40933

@karelz karelz closed this as completed Sep 9, 2019
@msftgits msftgits transferred this issue from dotnet/corefx Feb 1, 2020
@msftgits msftgits added this to the 5.0 milestone Feb 1, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 12, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants