Skip to content

Commit

Permalink
performance: make NetworkPacket a struct, move hot paths to ValueTask
Browse files Browse the repository at this point in the history
  • Loading branch information
jacksonrakena committed Dec 12, 2023
1 parent 5d08cc6 commit c3f43e4
Show file tree
Hide file tree
Showing 10 changed files with 95 additions and 90 deletions.
2 changes: 1 addition & 1 deletion Common/Networking/Channels/ChannelBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,6 @@ public async Task<bool> UpdateQueueAsync()
/// </summary>
/// <param name="packet"></param>
/// <returns></returns>
public abstract Task<bool> HandlePacketAsync(NetworkPacket packet);
public abstract ValueTask<bool> HandlePacketAsync(NetworkPacket packet);
}
}
24 changes: 13 additions & 11 deletions Common/Networking/Channels/ReliableChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ internal sealed class ReliableChannel : ChannelBase
private readonly byte _id;
private readonly bool _ordered;

private readonly NetworkPacket _outgoingAcks; //for send acks
private NetworkPacket _outgoingAcks; //for send acks
private readonly SemaphoreSlim _outgoingAcksSem = new(1, 1);
private readonly PendingReliablePacket[] _pendingPackets; //for unacked packets and duplicates

private readonly SemaphoreSlim _pendingPacketsSem = new(1, 1);

private readonly SemaphoreSlim _pendingPacketsSemaphore = new(1, 1);
private readonly NetworkPacket[]? _receivedPackets; //for order
private readonly NetworkPacket?[]? _receivedPackets; //for order
private readonly int _windowSize;

private int _localSequence;
Expand All @@ -43,7 +43,7 @@ public ReliableChannel(PeerBase peer, bool ordered, byte id) : base(peer)
if (_ordered)
{
_deliveryMethod = DeliveryMethod.ReliableOrdered;
_receivedPackets = new NetworkPacket[_windowSize];
_receivedPackets = new NetworkPacket?[_windowSize];
_earlyReceived = Array.Empty<bool>();
}
else
Expand All @@ -60,7 +60,7 @@ public ReliableChannel(PeerBase peer, bool ordered, byte id) : base(peer)
_outgoingAcks.ChannelId = id;
}

private async Task ProcessAckAsync(NetworkPacket packet)
private async ValueTask ProcessAckAsync(NetworkPacket packet)
{
if (packet.Data.Count != _outgoingAcks.Data.Count)
{
Expand Down Expand Up @@ -188,7 +188,7 @@ protected override async Task<bool> FlushQueueAsync()


//Process incoming packet
public override async Task<bool> HandlePacketAsync(NetworkPacket packet)
public override async ValueTask<bool> HandlePacketAsync(NetworkPacket packet)
{
if (packet.Property == PacketProperty.Ack)
{
Expand Down Expand Up @@ -238,7 +238,9 @@ public override async Task<bool> HandlePacketAsync(NetworkPacket packet)
{
//New window position
var newWindowStart = (_remoteWindowStart + relate - _windowSize + 1) % NetConstants.MaxSequence;
_outgoingAcks.Sequence = (ushort)newWindowStart;
var oa = _outgoingAcks;
oa.Sequence = (ushort)newWindowStart;
_outgoingAcks = oa;

//Clean old data
while (_remoteWindowStart != newWindowStart)
Expand Down Expand Up @@ -285,12 +287,12 @@ public override async Task<bool> HandlePacketAsync(NetworkPacket packet)

if (_ordered)
{
NetworkPacket p;
NetworkPacket? p;
while ((p = _receivedPackets[_remoteSequence % _windowSize]) != null)
{
//process holden packet
_receivedPackets[_remoteSequence % _windowSize] = null;
await Peer.AddReliablePacket(_deliveryMethod, p);
await Peer.AddReliablePacket(_deliveryMethod, p.Value);
_remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence;
}
}
Expand Down Expand Up @@ -329,7 +331,7 @@ private class PendingReliablePacket

public override string ToString()
{
return _packet == null ? "Empty" : _packet.Sequence.ToString();
return _packet == null ? "Empty" : _packet.Value.Sequence.ToString();
}

public void Init(NetworkPacket packet)
Expand All @@ -354,15 +356,15 @@ public async Task<bool> TrySendAsync(long utcNowTicks, PeerBase peer)

_timeStamp = utcNowTicks;
_isSent = true;
await peer.SendUserData(_packet);
await peer.SendUserData(_packet.Value);
return true;
}

public async Task<bool> ClearAsync(PeerBase peer)
{
if (_packet != null)
{
await peer.RecycleAndDeliver(_packet);
await peer.RecycleAndDeliver(_packet.Value);
_packet = null;
return true;
}
Expand Down
19 changes: 11 additions & 8 deletions Common/Networking/Channels/SequencedChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Promul.Common.Networking.Channels
{
internal sealed class SequencedChannel : ChannelBase
{
private readonly NetworkPacket? _ackPacket;
private NetworkPacket? _ackPacket;
private readonly byte _id;

private readonly SemaphoreSlim _outgoingQueueSem = new(1, 1);
Expand All @@ -24,8 +24,9 @@ public SequencedChannel(PeerBase peer, bool reliable, byte id) : base(peer)
_reliable = reliable;
if (_reliable)
{
_ackPacket = NetworkPacket.FromProperty(PacketProperty.Ack, 0);
_ackPacket.ChannelId = id;
var ap = NetworkPacket.FromProperty(PacketProperty.Ack, 0);
ap.ChannelId = id;
_ackPacket = ap;
}
}

Expand All @@ -41,7 +42,7 @@ protected override async Task<bool> FlushQueueAsync()
if (packet != null)
{
_lastPacketSendTime = currentTime;
await Peer.SendUserData(packet);
await Peer.SendUserData(packet.Value);
}
}
}
Expand Down Expand Up @@ -71,20 +72,22 @@ protected override async Task<bool> FlushQueueAsync()
if (_reliable && _mustSendAck)
{
_mustSendAck = false;
_ackPacket!.Sequence = _remoteSequence;
await Peer.SendUserData(_ackPacket);
var p = _ackPacket.Value;

Check warning on line 75 in Common/Networking/Channels/SequencedChannel.cs

View workflow job for this annotation

GitHub Actions / build

Nullable value type may be null.
p.Sequence = _remoteSequence;
_ackPacket = p;
await Peer.SendUserData(_ackPacket.Value);
}

return _lastPacket != null;
}

public override async Task<bool> HandlePacketAsync(NetworkPacket packet)
public override async ValueTask<bool> HandlePacketAsync(NetworkPacket packet)
{
if (packet.IsFragmented)
return false;
if (packet.Property == PacketProperty.Ack)
{
if (_reliable && _lastPacket != null && packet.Sequence == _lastPacket.Sequence)
if (_reliable && _lastPacket != null && packet.Sequence == _lastPacket.Value.Sequence)
_lastPacket = null;
return false;
}
Expand Down
20 changes: 10 additions & 10 deletions Common/Networking/Core/PromulManager.Events.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,27 @@ namespace Promul.Common.Networking
{
public partial class PromulManager
{
public delegate Task ConnectionlessReceiveEvent(IPEndPoint remoteEndPoint, CompositeReader reader,
public delegate ValueTask ConnectionlessReceiveEvent(IPEndPoint remoteEndPoint, CompositeReader reader,
UnconnectedMessageType messageType);

public delegate Task ConnectionRequestEvent(ConnectionRequest request);
public delegate ValueTask ConnectionRequestEvent(ConnectionRequest request);

public delegate Task DeliveryEvent(PeerBase peer, object? userData);
public delegate ValueTask DeliveryEvent(PeerBase peer, object? userData);

public delegate Task NetworkErrorEvent(IPEndPoint endPoint, SocketError socketError);
public delegate ValueTask NetworkErrorEvent(IPEndPoint endPoint, SocketError socketError);

public delegate Task NetworkLatencyUpdateEvent(PeerBase peer, int latency);
public delegate ValueTask NetworkLatencyUpdateEvent(PeerBase peer, int latency);

public delegate Task NetworkReceiveEvent(PeerBase peer, CompositeReader reader, byte channel,
public delegate ValueTask NetworkReceiveEvent(PeerBase peer, CompositeReader reader, byte channel,
DeliveryMethod deliveryMethod);

public delegate Task NtpResponseEvent(NtpPacket packet);
public delegate ValueTask NtpResponseEvent(NtpPacket packet);

public delegate Task PeerAddressChangedEvent(PeerBase peer, IPEndPoint previousAddress);
public delegate ValueTask PeerAddressChangedEvent(PeerBase peer, IPEndPoint previousAddress);

public delegate Task PeerConnectedEvent(PeerBase peer);
public delegate ValueTask PeerConnectedEvent(PeerBase peer);

public delegate Task PeerDisconnectedEvent(PeerBase peer, DisconnectInfo disconnectInfo);
public delegate ValueTask PeerDisconnectedEvent(PeerBase peer, DisconnectInfo disconnectInfo);

/// <summary>
/// Invoked when a connection is made with a remote peer.
Expand Down
10 changes: 5 additions & 5 deletions Common/Networking/Core/PromulManager.Socket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ private async Task<bool> ProcessError(SocketException ex)
return false;
}

private async Task ReceiveInternalAsync(Socket s, EndPoint bufferEndPoint, CancellationToken ct = default)
private async ValueTask ReceiveInternalAsync(Socket s, EndPoint bufferEndPoint, CancellationToken ct = default)
{
var receiveBuffer = new byte[NetConstants.MaxPacketSize];
var receive = await s.ReceiveFromAsync(receiveBuffer, SocketFlags.None, bufferEndPoint);
Expand All @@ -106,8 +106,8 @@ public async Task ListenAsync(CancellationToken cancellationToken = default)
await ReceiveInternalAsync(_udpSocketv4, bufferEndPoint4, cancellationToken);
else
await Task.WhenAll(
ReceiveInternalAsync(_udpSocketv4, bufferEndPoint4, cancellationToken),
ReceiveInternalAsync(_udpSocketv6, bufferEndPoint6, cancellationToken)
ReceiveInternalAsync(_udpSocketv4, bufferEndPoint4, cancellationToken).AsTask(),
ReceiveInternalAsync(_udpSocketv6, bufferEndPoint6, cancellationToken).AsTask()
);
}
catch (SocketException ex)
Expand Down Expand Up @@ -294,7 +294,7 @@ private bool BindInternal(Socket socket, IPEndPoint ep)
/// <param name="remoteEndPoint">The remote endpoint to send to.</param>
/// <param name="ct">The cancellation token used to cancel the send operation.</param>
/// <returns>The number of bytes sent.</returns>
internal async Task<int> RawSendAsync(ArraySegment<byte> data, IPEndPoint remoteEndPoint,
internal async ValueTask<int> RawSendAsync(ArraySegment<byte> data, IPEndPoint remoteEndPoint,
CancellationToken ct = default)
{
var socket = _udpSocketv4;
Expand Down Expand Up @@ -359,7 +359,7 @@ await ForceDisconnectPeerAsync(
return result;
}

public async Task<bool> SendBroadcast(ArraySegment<byte> data, int port)
public async ValueTask<bool> SendBroadcast(ArraySegment<byte> data, int port)
{
NetworkPacket packet;
if (_extraPacketLayer != null)
Expand Down
9 changes: 4 additions & 5 deletions Common/Networking/Core/PromulManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -426,10 +426,10 @@ private async Task ProcessConnectRequest(
if (OnConnectionRequest != null) await OnConnectionRequest(req);
}

internal async Task CreateReceiveEvent(NetworkPacket packet, DeliveryMethod method, byte channelNumber,
internal ValueTask CreateReceiveEvent(NetworkPacket packet, DeliveryMethod method, byte channelNumber,
int headerSize, PeerBase fromPeer)
{
if (OnReceive != null) await OnReceive(fromPeer, packet.CreateReader(headerSize), channelNumber, method);
return OnReceive?.Invoke(fromPeer, packet.CreateReader(headerSize), channelNumber, method) ?? default;
}

/// <summary>
Expand Down Expand Up @@ -655,7 +655,7 @@ private struct IncomingData
private const int MinLatencyThreshold = 5;
#endif

private async Task OnMessageReceived(NetworkPacket packet, IPEndPoint remoteEndPoint)
private async ValueTask OnMessageReceived(NetworkPacket packet, IPEndPoint remoteEndPoint)
{
#if DEBUG
if (SimulatePacketLoss && _randomGenerator.NextDouble() * 100 < SimulatePacketLossChance)
Expand All @@ -680,7 +680,7 @@ private async Task OnMessageReceived(NetworkPacket packet, IPEndPoint remoteEndP
await DebugMessageReceived(packet, remoteEndPoint);
}

private async Task DebugMessageReceived(NetworkPacket packet, IPEndPoint remoteEndPoint)
private async ValueTask DebugMessageReceived(NetworkPacket packet, IPEndPoint remoteEndPoint)
{
#endif
var originalPacketSize = packet.Data.Count;
Expand Down Expand Up @@ -731,7 +731,6 @@ private async Task DebugMessageReceived(NetworkPacket packet, IPEndPoint remoteE
if (!packet.Verify())
{
NetDebug.WriteError("[NM] DataReceived: bad!");
//PoolRecycle(packet);
return;
}

Expand Down
2 changes: 1 addition & 1 deletion Common/Networking/Packets/NetworkPacket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public enum PacketProperty : byte
InvalidProtocol
}

public class NetworkPacket
public readonly struct NetworkPacket
{
private static readonly int PropertiesCount = Enum.GetValues(typeof(PacketProperty)).Length;
private static readonly int[] HeaderSizes;
Expand Down
6 changes: 3 additions & 3 deletions Common/Networking/Peers/OutgoingPeer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ public class OutgoingPeer : PeerBase
{
private readonly NetworkPacket _connectRequestPacket;

public OutgoingPeer(PromulManager manager, IPEndPoint remote, int id, long connectTime, byte connectionNumber,
public OutgoingPeer(PromulManager manager, IPEndPoint remote, int id, byte connectionNumber,
ArraySegment<byte> data)
: base(manager, remote, id, connectTime, connectionNumber)
: base(manager, remote, id, DateTime.UtcNow.Ticks, connectionNumber)
{
var packet = NetConnectRequestPacket.Make(data, remote.Serialize(), connectTime, id);
var packet = NetConnectRequestPacket.Make(data, remote.Serialize(), ConnectTime, id);
packet.ConnectionNumber = connectionNumber;
ConnectionState = ConnectionState.Outgoing;
_connectRequestPacket = packet;
Expand Down
Loading

0 comments on commit c3f43e4

Please sign in to comment.