Runtime: Udp Socket.ReceiveFrom a lot of allocations for IPAdress

Created on 9 Jul 2019  路  5Comments  路  Source: dotnet/runtime

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

area-System.Net.Sockets

Most helpful comment

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.

All 5 comments

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.

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

```c#
class Socket
{
public int SendTo(ReadOnlySpan buffer, SocketFlags socketFlags, SocketAddress remoteAddress);
public ValueTask SendToAsync(ReadOnlyMemory 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:

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

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.

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.

Duplicate of dotnet/corefx#40933

Was this page helpful?
0 / 5 - 0 ratings