Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Buffers;
using System.IO.Pipelines;
using BenchmarkDotNet.Attributes;

namespace Microsoft.AspNetCore.Server.Kestrel.Microbenchmarks;

public class BufferWriterWriteNumericBenchmark
{
private const int Iterations = 1000;

private NullPipeWriter _pipeWriter;

[Params(5UL, 421UL, 4096UL, 1_048_576UL, 123_456_789_012UL)]
public ulong Number { get; set; }

[GlobalSetup]
public void Setup()
{
_pipeWriter = new NullPipeWriter();
}

[Benchmark(OperationsPerInvoke = Iterations)]
public void WriteNumeric()
{
var writer = new BufferWriter<PipeWriter>(_pipeWriter);
var number = Number;
for (var i = 0; i < Iterations; i++)
{
writer.WriteNumeric(number);
}
writer.Commit();
}
}

public class BufferWriterWriteAsciiBenchmark
{
private const int Iterations = 1000;

private NullPipeWriter _pipeWriter;
private string _asciiValue;

[Params(10, 30, 100)]
public int StringLength { get; set; }

[GlobalSetup]
public void Setup()
{
_pipeWriter = new NullPipeWriter();
_asciiValue = new string('a', StringLength);
}

[Benchmark(OperationsPerInvoke = Iterations)]
public void WriteAscii()
{
var writer = new BufferWriter<PipeWriter>(_pipeWriter);
var value = _asciiValue;
for (var i = 0; i < Iterations; i++)
{
writer.WriteAscii(value);
}
writer.Commit();
}
}
29 changes: 16 additions & 13 deletions src/Shared/ServerInfrastructure/BufferExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Globalization;
using System.IO.Pipelines;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
Expand Down Expand Up @@ -170,22 +171,24 @@ internal static void WriteNumeric(ref this BufferWriter<PipeWriter> bufferWriter
[MethodImpl(MethodImplOptions.NoInlining)]
private static void WriteNumericMultiWrite(ref this BufferWriter<PipeWriter> buffer, ulong number)
{
const byte AsciiDigitStart = (byte)'0';

var value = number;
var position = _maxULongByteLength;
var byteBuffer = NumericBytesScratch;
do
// Fast path, format directly into the available memory
if (number.TryFormat(buffer.Span, out var bytesWritten, provider: CultureInfo.InvariantCulture))
{
// Consider using Math.DivRem() if available
var quotient = value / 10;
byteBuffer[--position] = (byte)(AsciiDigitStart + (value - quotient * 10)); // 0x30 = '0'
value = quotient;
buffer.Advance(bytesWritten);
return;
}
while (value != 0);

var length = _maxULongByteLength - position;
buffer.Write(new ReadOnlySpan<byte>(byteBuffer, position, length));
WriteNumericAcrossSpans(ref buffer, number);
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static void WriteNumericAcrossSpans(ref BufferWriter<PipeWriter> buffer, ulong number)
{
// Not enough space left in the current span, format to a scratch buffer and write it across spans
var byteBuffer = NumericBytesScratch;
var formatted = number.TryFormat(byteBuffer, out var bytesWritten, provider: CultureInfo.InvariantCulture);
Debug.Assert(formatted);
buffer.Write(new ReadOnlySpan<byte>(byteBuffer, 0, bytesWritten));
}

internal static void WriteEncoded(ref this BufferWriter<PipeWriter> buffer, string data, Encoding encoding)
Expand Down
Loading