Perf - enumerate with struct

Conflicts:
	src/Microsoft.AspNet.Server.Kestrel/project.json
This commit is contained in:
Louis DeJardin 2015-07-16 21:57:33 -07:00 committed by Stephen Halter
parent ad089be477
commit 96b03ee212
7 changed files with 7024 additions and 57 deletions

View File

@ -10,9 +10,9 @@ namespace Microsoft.AspNet.Server.Kestrel.GeneratedCode
// To enable this option, right-click on the project and select the Properties menu item. In the Build tab select "Produce outputs on build".
public class KnownHeaders : ICompileModule
{
string Each<T>(IEnumerable<T> values, Func<T, string> formatter)
static string Each<T>(IEnumerable<T> values, Func<T, string> formatter)
{
return values.Select(formatter).Aggregate((a, b) => a + b + "\r\n");
return values.Select(formatter).Aggregate((a, b) => a + b);
}
class KnownHeader
@ -26,6 +26,12 @@ namespace Microsoft.AspNet.Server.Kestrel.GeneratedCode
}
public virtual void BeforeCompile(BeforeCompileContext context)
{
var syntaxTree = Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.ParseText(GeneratedFile());
context.Compilation = context.Compilation.AddSyntaxTrees(syntaxTree);
}
public static string GeneratedFile()
{
var commonHeaders = new[]
{
@ -71,7 +77,7 @@ namespace Microsoft.AspNet.Server.Kestrel.GeneratedCode
"Referer",
"Range",
"TE",
"Translage",
"Translate",
"User-Agent",
}).Select((header, index) => new KnownHeader
{
@ -113,7 +119,7 @@ namespace Microsoft.AspNet.Server.Kestrel.GeneratedCode
}
};
var syntaxTree = Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.ParseText($@"
return $@"
using System;
using System.Collections.Generic;
@ -122,11 +128,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
public partial class {loop.ClassName}
{{
long _bits = 0;
{Each(loop.Headers, header => "string[] _" + header.Identifier + ";")}
{Each(loop.Headers, header => @"
string[] _" + header.Identifier + ";")}
protected override int GetCountFast()
{{
var count = Unknown.Count;
var count = MaybeUnknown?.Count ?? 0;
{Each(loop.Headers, header => $@"
if ({header.TestBit()})
{{
@ -142,7 +149,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
{{{Each(loop.HeadersByLength, byLength => $@"
case {byLength.Key}:
{{{Each(byLength, header => $@"
if (0 == StringComparer.OrdinalIgnoreCase.Compare(key, ""{header.Name}""))
if (""{header.Name}"".Equals(key, StringComparison.OrdinalIgnoreCase))
{{
if ({header.TestBit()})
{{
@ -156,7 +163,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
")}}}
break;
")}}}
return Unknown[key];
if (MaybeUnknown == null)
{{
throw new System.Collections.Generic.KeyNotFoundException();
}}
return MaybeUnknown[key];
}}
protected override bool TryGetValueFast(string key, out string[] value)
@ -165,7 +176,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
{{{Each(loop.HeadersByLength, byLength => $@"
case {byLength.Key}:
{{{Each(byLength, header => $@"
if (0 == StringComparer.OrdinalIgnoreCase.Compare(key, ""{header.Name}""))
if (""{header.Name}"".Equals(key, StringComparison.OrdinalIgnoreCase))
{{
if ({header.TestBit()})
{{
@ -181,7 +192,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
")}}}
break;
")}}}
return Unknown.TryGetValue(key, out value);
value = null;
return MaybeUnknown?.TryGetValue(key, out value) ?? false;
}}
protected override void SetValueFast(string key, string[] value)
@ -190,7 +202,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
{{{Each(loop.HeadersByLength, byLength => $@"
case {byLength.Key}:
{{{Each(byLength, header => $@"
if (0 == StringComparer.OrdinalIgnoreCase.Compare(key, ""{header.Name}""))
if (""{header.Name}"".Equals(key, StringComparison.OrdinalIgnoreCase))
{{
{header.SetBit()};
_{header.Identifier} = value;
@ -208,7 +220,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
{{{Each(loop.HeadersByLength, byLength => $@"
case {byLength.Key}:
{{{Each(byLength, header => $@"
if (0 == StringComparer.OrdinalIgnoreCase.Compare(key, ""{header.Name}""))
if (""{header.Name}"".Equals(key, StringComparison.OrdinalIgnoreCase))
{{
if ({header.TestBit()})
{{
@ -230,7 +242,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
{{{Each(loop.HeadersByLength, byLength => $@"
case {byLength.Key}:
{{{Each(byLength, header => $@"
if (0 == StringComparer.OrdinalIgnoreCase.Compare(key, ""{header.Name}""))
if (""{header.Name}"".Equals(key, StringComparison.OrdinalIgnoreCase))
{{
if ({header.TestBit()})
{{
@ -245,13 +257,13 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
")}}}
break;
")}}}
return Unknown.Remove(key);
return MaybeUnknown?.Remove(key) ?? false;
}}
protected override void ClearFast()
{{
_bits = 0;
Unknown.Clear();
MaybeUnknown?.Clear();
}}
protected override void CopyToFast(KeyValuePair<string, string[]>[] array, int arrayIndex)
@ -273,27 +285,44 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
++arrayIndex;
}}
")}
((ICollection<KeyValuePair<string, string[]>>)Unknown).CopyTo(array, arrayIndex);
((ICollection<KeyValuePair<string, string[]>>)MaybeUnknown)?.CopyTo(array, arrayIndex);
}}
protected override IEnumerable<KeyValuePair<string, string[]>> EnumerateFast()
public partial struct Enumerator
{{
{Each(loop.Headers, header => $@"
if ({header.TestBit()})
{{
yield return new KeyValuePair<string, string[]>(""{header.Name}"", _{header.Identifier});
}}
")}
foreach(var kv in Unknown)
public bool MoveNext()
{{
yield return kv;
switch (_state)
{{
{Each(loop.Headers, header => $@"
case {header.Index}:
goto state{header.Index};
")}
default:
goto state_default;
}}
{Each(loop.Headers, header => $@"
state{header.Index}:
if ({header.TestBit()})
{{
_current = new KeyValuePair<string, string[]>(""{header.Name}"", _collection._{header.Identifier});
_state = {header.Index + 1};
return true;
}}
")}
state_default:
if (!_hasUnknown || !_unknownEnumerator.MoveNext())
{{
_current = default(KeyValuePair<string, string[]>);
return false;
}}
_current = _unknownEnumerator.Current;
return true;
}}
}}
}}
")}}}
");
context.Compilation = context.Compilation.AddSyntaxTrees(syntaxTree);
";
}
public virtual void AfterCompile(AfterCompileContext context)

View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Server.Kestrel.GeneratedCode
{
public class Program
{
public int Main(string[] args)
{
var text = KnownHeaders.GeneratedFile();
if (args.Length == 1)
{
var existing = File.Exists(args[0]) ? File.ReadAllText(args[0]) : "";
if (!string.Equals(text, existing))
{
File.WriteAllText(args[0], text);
}
}
else
{
Console.WriteLine(text);
}
return 0;
}
}
}

View File

@ -27,6 +27,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
private bool _resultStarted;
private bool _responseStarted;
private bool _keepAlive;
private readonly FrameRequestHeaders _requestHeaders = new FrameRequestHeaders();
private readonly FrameResponseHeaders _responseHeaders = new FrameResponseHeaders();
/*
//IDictionary<string, object> _environment;
@ -43,8 +45,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
{
FrameControl = this;
StatusCode = 200;
RequestHeaders = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
ResponseHeaders = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
RequestHeaders = _requestHeaders;
ResponseHeaders = _responseHeaders;
}
public string Method { get; set; }

File diff suppressed because it is too large Load Diff

View File

@ -5,17 +5,11 @@ using System.Linq;
namespace Microsoft.AspNet.Server.Kestrel.Http
{
public partial class FrameRequestHeaders : FrameHeaders
{
}
public partial class FrameResponseHeaders : FrameHeaders
{
}
public abstract class FrameHeaders : IDictionary<string, string[]>
{
protected Dictionary<string, string[]> Unknown = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
protected Dictionary<string, string[]> MaybeUnknown;
protected Dictionary<string, string[]> Unknown => MaybeUnknown ?? (MaybeUnknown = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase));
protected virtual int GetCountFast()
{ throw new NotImplementedException(); }
@ -41,7 +35,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
protected virtual void CopyToFast(KeyValuePair<string, string[]>[] array, int arrayIndex)
{ throw new NotImplementedException(); }
protected virtual IEnumerable<KeyValuePair<string, string[]>> EnumerateFast()
protected virtual IEnumerator<KeyValuePair<string, string[]>> GetEnumeratorFast()
{ throw new NotImplementedException(); }
@ -62,9 +56,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
bool ICollection<KeyValuePair<string, string[]>>.IsReadOnly => false;
ICollection<string> IDictionary<string, string[]>.Keys => EnumerateFast().Select(x => x.Key).ToList();
ICollection<string> IDictionary<string, string[]>.Keys => ((IDictionary<string,string[]>)this).Select(x => x.Key).ToList();
ICollection<string[]> IDictionary<string, string[]>.Values => EnumerateFast().Select(x => x.Value).ToList();
ICollection<string[]> IDictionary<string, string[]>.Values => ((IDictionary<string, string[]>)this).Select(x => x.Value).ToList();
void ICollection<KeyValuePair<string, string[]>>.Add(KeyValuePair<string, string[]> item)
{
@ -102,12 +96,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
IEnumerator IEnumerable.GetEnumerator()
{
return EnumerateFast().GetEnumerator();
return GetEnumeratorFast();
}
IEnumerator<KeyValuePair<string, string[]>> IEnumerable<KeyValuePair<string, string[]>>.GetEnumerator()
{
return EnumerateFast().GetEnumerator();
return GetEnumeratorFast();
}
bool ICollection<KeyValuePair<string, string[]>>.Remove(KeyValuePair<string, string[]> item)
@ -129,4 +123,100 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
return TryGetValueFast(key, out value);
}
}
public partial class FrameRequestHeaders : FrameHeaders
{
public Enumerator GetEnumerator()
{
return new Enumerator(this);
}
protected override IEnumerator<KeyValuePair<string, string[]>> GetEnumeratorFast()
{
return GetEnumerator();
}
public partial struct Enumerator : IEnumerator<KeyValuePair<string, string[]>>
{
FrameRequestHeaders _collection;
long _bits;
int _state;
KeyValuePair<string, string[]> _current;
bool _hasUnknown;
Dictionary<string, string[]>.Enumerator _unknownEnumerator;
internal Enumerator(FrameRequestHeaders collection)
{
_collection = collection;
_bits = collection._bits;
_state = 0;
_current = default(KeyValuePair<string, string[]>);
_hasUnknown = collection.MaybeUnknown != null;
_unknownEnumerator = _hasUnknown
? collection.MaybeUnknown.GetEnumerator()
: default(Dictionary<string, string[]>.Enumerator);
}
public KeyValuePair<string, string[]> Current => _current;
object IEnumerator.Current => _current;
public void Dispose()
{
}
public void Reset()
{
_state = 0;
}
}
}
public partial class FrameResponseHeaders : FrameHeaders
{
public Enumerator GetEnumerator()
{
return new Enumerator(this);
}
protected override IEnumerator<KeyValuePair<string, string[]>> GetEnumeratorFast()
{
return GetEnumerator();
}
public partial struct Enumerator : IEnumerator<KeyValuePair<string, string[]>>
{
FrameResponseHeaders _collection;
long _bits;
int _state;
KeyValuePair<string, string[]> _current;
bool _hasUnknown;
Dictionary<string, string[]>.Enumerator _unknownEnumerator;
internal Enumerator(FrameResponseHeaders collection)
{
_collection = collection;
_bits = collection._bits;
_state = 0;
_current = default(KeyValuePair<string, string[]>);
_hasUnknown = collection.MaybeUnknown != null;
_unknownEnumerator = _hasUnknown
? collection.MaybeUnknown.GetEnumerator()
: default(Dictionary<string, string[]>.Enumerator);
}
public KeyValuePair<string, string[]> Current => _current;
object IEnumerator.Current => _current;
public void Dispose()
{
}
public void Reset()
{
_state = 0;
}
}
}
}

View File

@ -1,11 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Server.Kestrel
{
public class KnownHeaders : Microsoft.AspNet.Server.Kestrel.GeneratedCode.KnownHeaders
{
}
}

View File

@ -6,8 +6,7 @@
"url": "git://github.com/aspnet/kestrelhttpserver"
},
"dependencies": {
"Microsoft.Dnx.Runtime.Abstractions": "1.0.0-beta7-*",
"Microsoft.AspNet.Server.Kestrel.GeneratedCode": { "version": "1.0.0-*", "type": "build" }
"Microsoft.Dnx.Runtime.Abstractions": "1.0.0-beta7-*"
},
"frameworks": {
"dnx451": { },
@ -33,5 +32,8 @@
},
"compilationOptions": {
"allowUnsafe": true
},
"scripts": {
"prepare": "dnx ../Microsoft.AspNet.Server.Kestrel.GeneratedCode run Http/FrameHeaders.Generated.cs"
}
}