diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.csproj b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.csproj index 746c59cd10..e010d7fce2 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.csproj +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.csproj @@ -1,4 +1,4 @@ - + @@ -6,6 +6,7 @@ netcoreapp1.1 Exe true + true false diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/RequestParsing.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/RequestParsing.cs index 90bd7a16c5..8fc4bc0eea 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/RequestParsing.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/RequestParsing.cs @@ -95,7 +95,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance private void InsertData(byte[] bytes) { var buffer = Pipe.Writer.Alloc(2048); - buffer.Write(bytes); + buffer.WriteFast(bytes); // There should not be any backpressure and task completes immediately buffer.FlushAsync().GetAwaiter().GetResult(); } diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/WritableBufferExtensions.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/WritableBufferExtensions.cs new file mode 100644 index 0000000000..04a538e4be --- /dev/null +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/WritableBufferExtensions.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.IO.Pipelines; +using System.Text; + +namespace Microsoft.AspNetCore.Server.Kestrel.Performance +{ + internal static class WritableBufferExtensions + { + public static void WriteFast(this WritableBuffer buffer, ReadOnlySpan source) + { + if (buffer.Memory.IsEmpty) + { + buffer.Ensure(); + } + + // Fast path, try copying to the available memory directly + if (source.Length <= buffer.Memory.Length) + { + source.CopyToFast(buffer.Memory.Span); + buffer.Advance(source.Length); + return; + } + + var remaining = source.Length; + var offset = 0; + + while (remaining > 0) + { + var writable = Math.Min(remaining, buffer.Memory.Length); + + buffer.Ensure(writable); + + if (writable == 0) + { + continue; + } + + source.Slice(offset, writable).CopyToFast(buffer.Memory.Span); + + remaining -= writable; + offset += writable; + + buffer.Advance(writable); + } + } + + private unsafe static void CopyToFast(this ReadOnlySpan source, Span destination) + { + if (destination.Length < source.Length) + { + throw new InvalidOperationException(); + } + + // Assume it fits + fixed (byte* pSource = &source.DangerousGetPinnableReference()) + fixed (byte* pDest = &destination.DangerousGetPinnableReference()) + { + Buffer.MemoryCopy(pSource, pDest, destination.Length, source.Length); + } + } + } +}