Skip to content
This repository has been archived by the owner on Dec 18, 2018. It is now read-only.

Commit

Permalink
Go faster stripes MkII
Browse files Browse the repository at this point in the history
Timings
Lower alloc
MemoryPool removal, MemoryPool2 rename
Memory pool SocketOutput
Output buffer packing
Release all the locks
Ascii headers; MemoryTextWriter removal
Pool write requests
  • Loading branch information
benaadams committed Oct 31, 2015
1 parent facf3ad commit 32e7d7d
Show file tree
Hide file tree
Showing 31 changed files with 730 additions and 836 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class FilteredStreamAdapter

public FilteredStreamAdapter(
Stream filteredStream,
MemoryPool2 memory,
MemoryPool memory,
IKestrelTrace logger)
{
SocketInput = new SocketInput(memory);
Expand Down
6 changes: 3 additions & 3 deletions src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ public Connection(ListenerContext context, UvStreamHandle socket) : base(context

_connectionId = Interlocked.Increment(ref _lastConnectionId);

_rawSocketInput = new SocketInput(Memory2);
_rawSocketOutput = new SocketOutput(Thread, _socket, _connectionId, Log);
_rawSocketInput = new SocketInput(InputMemory);
_rawSocketOutput = new SocketOutput(OutputMemory, Thread, _socket, _connectionId, Log);
}

public void Start()
Expand Down Expand Up @@ -100,7 +100,7 @@ public void Start()

private void ApplyConnectionFilter()
{
var filteredStreamAdapter = new FilteredStreamAdapter(_filterContext.Connection, Memory2, Log);
var filteredStreamAdapter = new FilteredStreamAdapter(_filterContext.Connection, InputMemory, Log);

SocketInput = filteredStreamAdapter.SocketInput;
SocketOutput = filteredStreamAdapter.SocketOutput;
Expand Down
122 changes: 86 additions & 36 deletions src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,23 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
public partial class Frame : FrameContext, IFrameControl
{
private static readonly Encoding _ascii = Encoding.ASCII;
private static readonly ArraySegment<byte> _endChunkBytes = CreateAsciiByteArraySegment("\r\n");
private static readonly ArraySegment<byte> _endLineBytes = CreateAsciiByteArraySegment("\r\n");
private static readonly ArraySegment<byte> _endChunkBytes = _endLineBytes;
private static readonly ArraySegment<byte> _headerDelimiterBytes = CreateAsciiByteArraySegment(": ");
private static readonly ArraySegment<byte> _spaceBytes = CreateAsciiByteArraySegment(" ");
private static readonly ArraySegment<byte> _endChunkedResponseBytes = CreateAsciiByteArraySegment("0\r\n\r\n");
private static readonly ArraySegment<byte> _continueBytes = CreateAsciiByteArraySegment("HTTP/1.1 100 Continue\r\n\r\n");
private static readonly ArraySegment<byte> _contentLengthZeroBytes = CreateAsciiByteArraySegment("Content-Length: 0\r\n");
private static readonly ArraySegment<byte> _transferEncodingChunkedBytes = CreateAsciiByteArraySegment("Transfer-Encoding: chunked\r\n");
private static readonly ArraySegment<byte> _connectionCloseBytes = CreateAsciiByteArraySegment("Connection: close\r\n\r\n");
private static readonly ArraySegment<byte> _connectionKeepAliveBytes = CreateAsciiByteArraySegment("Connection: keep-alive\r\n\r\n");
private static readonly ArraySegment<byte> _emptyData = new ArraySegment<byte>(new byte[0]);
private static readonly byte[] _hex = Encoding.ASCII.GetBytes("0123456789abcdef");

private readonly object _onStartingSync = new Object();
private readonly object _onCompletedSync = new Object();
private readonly object _onStartingSync = new object();
private readonly object _onCompletedSync = new object();
private readonly FrameRequestHeaders _requestHeaders = new FrameRequestHeaders();
private readonly byte[] _nullBuffer = new byte[4096];
private readonly byte[] _scratchBuffer = new byte[4096];
private readonly FrameResponseHeaders _responseHeaders = new FrameResponseHeaders();

private List<KeyValuePair<Func<object, Task>, object>> _onStarting;
Expand Down Expand Up @@ -232,7 +239,7 @@ public async Task RequestProcessingAsync()

await ProduceEnd();

while (await RequestBody.ReadAsync(_nullBuffer, 0, _nullBuffer.Length) != 0)
while (await RequestBody.ReadAsync(_scratchBuffer, 0, _scratchBuffer.Length) != 0)
{
// Finish reading the request body in case the app did not.
}
Expand Down Expand Up @@ -471,19 +478,14 @@ public async Task ProduceStartAndFireOnStarting(bool immediate = true)
await ProduceStart(immediate, appCompleted: false);
}

private async Task ProduceStart(bool immediate, bool appCompleted)
private Task ProduceStart(bool immediate, bool appCompleted)
{
if (_responseStarted) return;
if (_responseStarted) return TaskUtilities.CompletedTask;
_responseStarted = true;

var status = ReasonPhrases.ToStatus(StatusCode, ReasonPhrase);

var responseHeader = CreateResponseHeader(status, appCompleted);

using (responseHeader.Item2)
{
await SocketOutput.WriteAsync(responseHeader.Item1, immediate: immediate);
}
return CreateResponseHeader(status, appCompleted, immediate);
}

private async Task ProduceEnd()
Expand Down Expand Up @@ -521,16 +523,54 @@ private async Task ProduceEnd()
}
}

private Tuple<ArraySegment<byte>, IDisposable> CreateResponseHeader(
ArraySegment<byte> ShortAsciiToBytes(string input)
{

var scratch = _scratchBuffer;
var len = input.Length;

var i = 0;
for (; i < scratch.Length; i++)
{
if (i >= len)
{
break;
}
scratch[i] = (byte)input[i];
}
var buffer = new ArraySegment<byte>(scratch, 0, i);
return buffer;
}
bool LongAsciiToBytes(string input, int offset, out int newOffset, out ArraySegment<byte> buffer)
{
var scratch = _scratchBuffer;
var len = input.Length;

newOffset = offset;
var i = 0;
for (; i < scratch.Length; i++)
{
if (newOffset >= len)
{
break;
}
scratch[i] = (byte)input[newOffset];
newOffset++;
}

buffer = new ArraySegment<byte>(scratch, 0, i);
return newOffset < len;
}

private Task CreateResponseHeader(
string status,
bool appCompleted)
bool appCompleted,
bool immediate)
{
var writer = new MemoryPoolTextWriter(Memory);
writer.Write(HttpVersion);
writer.Write(' ');
writer.Write(status);
writer.Write('\r');
writer.Write('\n');
SocketOutput.Write(ShortAsciiToBytes(HttpVersion), immediate: false);
SocketOutput.Write(_spaceBytes, immediate: false);
SocketOutput.Write(ShortAsciiToBytes(status), immediate: false);
SocketOutput.Write(_endLineBytes, immediate: false);

var hasConnection = false;
var hasTransferEncoding = false;
Expand All @@ -555,21 +595,33 @@ private Tuple<ArraySegment<byte>, IDisposable> CreateResponseHeader(
hasContentLength = true;
}

ArraySegment<byte> buffer;
int inputOffset;
foreach (var value in header.Value)
{
writer.Write(header.Key);
writer.Write(':');
writer.Write(' ');
writer.Write(value);
writer.Write('\r');
writer.Write('\n');
inputOffset = 0;
while (LongAsciiToBytes(header.Key, inputOffset, out inputOffset, out buffer))
{
SocketOutput.Write(buffer, immediate: false);
}
SocketOutput.Write(buffer, immediate: false);

SocketOutput.Write(_headerDelimiterBytes, immediate: false);

inputOffset = 0;
while (LongAsciiToBytes(value, inputOffset, out inputOffset, out buffer))
{
SocketOutput.Write(buffer, immediate: false);
}
SocketOutput.Write(buffer, immediate: false);

SocketOutput.Write(_endLineBytes, immediate: false);

if (isConnection && value.IndexOf("close", StringComparison.OrdinalIgnoreCase) != -1)
{
_keepAlive = false;
}
}

}

if (_keepAlive && !hasTransferEncoding && !hasContentLength)
Expand All @@ -582,15 +634,15 @@ private Tuple<ArraySegment<byte>, IDisposable> CreateResponseHeader(
{
// Since the app has completed and we are only now generating
// the headers we can safely set the Content-Length to 0.
writer.Write("Content-Length: 0\r\n");
SocketOutput.Write(_contentLengthZeroBytes, immediate: false);
}
}
else
{
if (HttpVersion == "HTTP/1.1")
{
_autoChunk = true;
writer.Write("Transfer-Encoding: chunked\r\n");
SocketOutput.Write(_transferEncodingChunkedBytes, immediate: false);
}
else
{
Expand All @@ -601,19 +653,17 @@ private Tuple<ArraySegment<byte>, IDisposable> CreateResponseHeader(

if (_keepAlive == false && hasConnection == false && HttpVersion == "HTTP/1.1")
{
writer.Write("Connection: close\r\n\r\n");
return SocketOutput.WriteAsync(_connectionCloseBytes, immediate: immediate);
}
else if (_keepAlive && hasConnection == false && HttpVersion == "HTTP/1.0")
{
writer.Write("Connection: keep-alive\r\n\r\n");
return SocketOutput.WriteAsync(_connectionKeepAliveBytes, immediate: immediate);
}
else
{
writer.Write('\r');
writer.Write('\n');
return SocketOutput.WriteAsync(_endLineBytes, immediate: immediate);
}
writer.Flush();
return new Tuple<ArraySegment<byte>, IDisposable>(writer.Buffer, writer);

}

private bool TakeStartLine(SocketInput input)
Expand Down
13 changes: 9 additions & 4 deletions src/Microsoft.AspNet.Server.Kestrel/Http/ListenerContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ public class ListenerContext : ServiceContext
{
public ListenerContext()
{
Memory2 = new MemoryPool2();
InputMemory = new MemoryPool();
OutputMemory = new MemoryPool();
}

public ListenerContext(ServiceContext serviceContext)
: base(serviceContext)
{
Memory2 = new MemoryPool2();
InputMemory = new MemoryPool();
OutputMemory = new MemoryPool();
}

public ListenerContext(ListenerContext listenerContext)
Expand All @@ -25,7 +27,8 @@ public ListenerContext(ListenerContext listenerContext)
ServerAddress = listenerContext.ServerAddress;
Thread = listenerContext.Thread;
Application = listenerContext.Application;
Memory2 = listenerContext.Memory2;
InputMemory = listenerContext.InputMemory;
OutputMemory = listenerContext.OutputMemory;
Log = listenerContext.Log;
}

Expand All @@ -35,6 +38,8 @@ public ListenerContext(ListenerContext listenerContext)

public RequestDelegate Application { get; set; }

public MemoryPool2 Memory2 { get; set; }
public MemoryPool InputMemory { get; set; }

public MemoryPool OutputMemory { get; set; }
}
}
9 changes: 6 additions & 3 deletions src/Microsoft.AspNet.Server.Kestrel/Http/ListenerPrimary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ abstract public class ListenerPrimary : Listener

// this message is passed to write2 because it must be non-zero-length,
// but it has no other functional significance
private readonly ArraySegment<ArraySegment<byte>> _dummyMessage = new ArraySegment<ArraySegment<byte>>(new[] { new ArraySegment<byte>(new byte[] { 1, 2, 3, 4 }) });
private readonly byte[] _dummyBuffer = new byte[] { 1, 2, 3, 4 };

protected ListenerPrimary(ServiceContext serviceContext) : base(serviceContext)
{
Expand Down Expand Up @@ -80,14 +80,17 @@ protected override void DispatchConnection(UvStreamHandle socket)
}
else
{
var msg = MemoryPoolBlock.Create(new ArraySegment<byte>(_dummyBuffer), IntPtr.Zero, null, null);
msg.End = msg.Start + _dummyBuffer.Length;

var dispatchPipe = _dispatchPipes[index];
var write = new UvWriteReq(Log);
write.Init(Thread.Loop);
write.Write2(
dispatchPipe,
_dummyMessage,
new ArraySegment<MemoryPoolBlock>(new[] { msg }),
socket,
(write2, status, error, state) =>
(write2, status, error, bytesWritten, state) =>
{
write2.Dispose();
((UvStreamHandle)state).Dispose();
Expand Down
Loading

0 comments on commit 32e7d7d

Please sign in to comment.