Skip to content

Commit 0c0474d

Browse files
committed
Rewrite ReadPayloadAsync without recursive continuations.
This produces a 20% reduction in allocations in the "ManyRowsNewSync" scenario.
1 parent 1527699 commit 0c0474d

File tree

1 file changed

+36
-8
lines changed

1 file changed

+36
-8
lines changed

src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,18 +51,41 @@ public static ValueTask<Packet> ReadPacketAsync(BufferedByteReader bufferedByteR
5151

5252
public static ValueTask<ArraySegment<byte>> ReadPayloadAsync(BufferedByteReader bufferedByteReader, IByteHandler byteHandler, Func<int?> getNextSequenceNumber, ArraySegment<byte> previousPayloads, ProtocolErrorBehavior protocolErrorBehavior, IOBehavior ioBehavior)
5353
{
54-
return ReadPacketAsync(bufferedByteReader, byteHandler, getNextSequenceNumber, protocolErrorBehavior, ioBehavior).ContinueWith(packet =>
55-
ContinueRead(bufferedByteReader, byteHandler, getNextSequenceNumber, previousPayloads, packet, protocolErrorBehavior, ioBehavior));
54+
var readPacketTask = ReadPacketAsync(bufferedByteReader, byteHandler, getNextSequenceNumber, protocolErrorBehavior, ioBehavior);
55+
while (readPacketTask.IsCompleted)
56+
{
57+
ValueTask<ArraySegment<byte>> result;
58+
if (HasReadPayload(ref previousPayloads, readPacketTask.Result, protocolErrorBehavior, out result))
59+
return result;
60+
61+
readPacketTask = ReadPacketAsync(bufferedByteReader, byteHandler, getNextSequenceNumber, protocolErrorBehavior, ioBehavior);
62+
}
63+
64+
return AddContinuation(readPacketTask, bufferedByteReader, byteHandler, getNextSequenceNumber, previousPayloads, protocolErrorBehavior, ioBehavior);
65+
66+
// NOTE: use a local function (with no captures) to defer creation of lambda objects
67+
ValueTask<ArraySegment<byte>> AddContinuation(ValueTask<Packet> readPacketTask_, BufferedByteReader bufferedByteReader_, IByteHandler byteHandler_, Func<int?> getNextSequenceNumber_, ArraySegment<byte> previousPayloads_, ProtocolErrorBehavior protocolErrorBehavior_, IOBehavior ioBehavior_)
68+
{
69+
return readPacketTask_.ContinueWith(packet =>
70+
HasReadPayload(ref previousPayloads_, packet, protocolErrorBehavior_, out var result_) ? result_ :
71+
ReadPayloadAsync(bufferedByteReader_, byteHandler_, getNextSequenceNumber_, previousPayloads_, protocolErrorBehavior_, ioBehavior_));
72+
}
5673
}
5774

58-
private static ValueTask<ArraySegment<byte>> ContinueRead(BufferedByteReader bufferedByteReader, IByteHandler byteHandler, Func<int?> getNextSequenceNumber, ArraySegment<byte> previousPayloads, Packet packet, ProtocolErrorBehavior protocolErrorBehavior, IOBehavior ioBehavior)
75+
private static bool HasReadPayload(ref ArraySegment<byte> previousPayloads, Packet packet, ProtocolErrorBehavior protocolErrorBehavior, out ValueTask<ArraySegment<byte>> result)
5976
{
6077
if (packet == null && protocolErrorBehavior == ProtocolErrorBehavior.Ignore)
61-
return default(ValueTask<ArraySegment<byte>>);
78+
{
79+
result = default(ValueTask<ArraySegment<byte>>);
80+
return true;
81+
}
6282

6383
var previousPayloadsArray = previousPayloads.Array;
6484
if (previousPayloadsArray == null && packet.Contents.Count < MaxPacketSize)
65-
return new ValueTask<ArraySegment<byte>>(packet.Contents);
85+
{
86+
result = new ValueTask<ArraySegment<byte>>(packet.Contents);
87+
return true;
88+
}
6689

6790
if (previousPayloadsArray == null)
6891
previousPayloadsArray = new byte[ProtocolUtility.MaxPacketSize + 1];
@@ -72,9 +95,14 @@ private static ValueTask<ArraySegment<byte>> ContinueRead(BufferedByteReader buf
7295
Buffer.BlockCopy(packet.Contents.Array, packet.Contents.Offset, previousPayloadsArray, previousPayloads.Offset + previousPayloads.Count, packet.Contents.Count);
7396
previousPayloads = new ArraySegment<byte>(previousPayloadsArray, previousPayloads.Offset, previousPayloads.Count + packet.Contents.Count);
7497

75-
return packet.Contents.Count < ProtocolUtility.MaxPacketSize ?
76-
new ValueTask<ArraySegment<byte>>(previousPayloads) :
77-
ReadPayloadAsync(bufferedByteReader, byteHandler, getNextSequenceNumber, previousPayloads, protocolErrorBehavior, ioBehavior);
98+
if (packet.Contents.Count < ProtocolUtility.MaxPacketSize)
99+
{
100+
result = new ValueTask<ArraySegment<byte>>(previousPayloads);
101+
return true;
102+
}
103+
104+
result = default(ValueTask<ArraySegment<byte>>);
105+
return false;
78106
}
79107

80108
public static ValueTask<int> WritePayloadAsync(IByteHandler byteHandler, Func<int> getNextSequenceNumber, ArraySegment<byte> payload, IOBehavior ioBehavior)

0 commit comments

Comments
 (0)