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

Commit 8b73e6d

Browse files
committed
stringpool->cache; iface for cache
1 parent 528b13a commit 8b73e6d

File tree

7 files changed

+66
-45
lines changed

7 files changed

+66
-45
lines changed

src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public abstract partial class Frame : FrameContext, IFrameControl
7070
private readonly Action<IFeatureCollection> _prepareRequest;
7171

7272
private readonly string _pathBase;
73-
protected readonly StringPool _stringPool = new StringPool();
73+
protected readonly IStringCache _stringCache = new StringCache();
7474

7575
public Frame(ConnectionContext context)
7676
: this(context, remoteEndPoint: null, localEndPoint: null, prepareRequest: null)
@@ -646,7 +646,7 @@ protected bool TakeStartLine(SocketInput input)
646646
{
647647
return false;
648648
}
649-
var method = begin.GetAsciiString(scan, _stringPool);
649+
var method = begin.GetAsciiString(scan, _stringCache);
650650

651651
scan.Take();
652652
begin = scan;
@@ -670,7 +670,7 @@ protected bool TakeStartLine(SocketInput input)
670670
{
671671
return false;
672672
}
673-
queryString = begin.GetAsciiString(scan, _stringPool);
673+
queryString = begin.GetAsciiString(scan, _stringCache);
674674
}
675675

676676
scan.Take();
@@ -679,7 +679,7 @@ protected bool TakeStartLine(SocketInput input)
679679
{
680680
return false;
681681
}
682-
var httpVersion = begin.GetAsciiString(scan, _stringPool);
682+
var httpVersion = begin.GetAsciiString(scan, _stringCache);
683683

684684
scan.Take();
685685
if (scan.Take() != '\n')
@@ -700,7 +700,7 @@ protected bool TakeStartLine(SocketInput input)
700700
else
701701
{
702702
// URI wasn't encoded, parse as ASCII
703-
requestUrlPath = pathBegin.GetAsciiString(pathEnd, _stringPool);
703+
requestUrlPath = pathBegin.GetAsciiString(pathEnd, _stringCache);
704704
}
705705

706706
consumed = scan;
@@ -753,7 +753,7 @@ private bool RequestUrlStartsWithPathBase(string requestUrl, out bool caseMatche
753753
return true;
754754
}
755755

756-
public static bool TakeMessageHeaders(SocketInput input, FrameRequestHeaders requestHeaders, StringPool stringPool)
756+
public static bool TakeMessageHeaders(SocketInput input, FrameRequestHeaders requestHeaders, IStringCache stringCache)
757757
{
758758
var scan = input.ConsumingStart();
759759
var consumed = scan;
@@ -844,7 +844,7 @@ public static bool TakeMessageHeaders(SocketInput input, FrameRequestHeaders req
844844
}
845845

846846
var name = beginName.GetArraySegment(endName);
847-
var value = beginValue.GetAsciiString(endValue, stringPool);
847+
var value = beginValue.GetAsciiString(endValue, stringCache);
848848
if (wrapping)
849849
{
850850
value = value.Replace("\r\n", " ");

src/Microsoft.AspNet.Server.Kestrel/Http/FrameOfT.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public override async Task RequestProcessingAsync()
4343
var terminated = false;
4444
while (!terminated && !_requestProcessingStopping)
4545
{
46-
_stringPool.MarkStart();
46+
_stringCache?.MarkStart();
4747

4848
while (!terminated && !_requestProcessingStopping && !TakeStartLine(SocketInput))
4949
{
@@ -54,7 +54,7 @@ public override async Task RequestProcessingAsync()
5454
}
5555
}
5656

57-
while (!terminated && !_requestProcessingStopping && !TakeMessageHeaders(SocketInput, _requestHeaders, _stringPool))
57+
while (!terminated && !_requestProcessingStopping && !TakeMessageHeaders(SocketInput, _requestHeaders, _stringCache))
5858
{
5959
terminated = SocketInput.RemoteIntakeFin;
6060
if (!terminated)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace Microsoft.AspNet.Server.Kestrel.Infrastructure
2+
{
3+
public interface IStringCache
4+
{
5+
void MarkStart();
6+
unsafe string GetString(char* data, uint hash, int length);
7+
}
8+
}

src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2Extensions.cs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@ static MemoryPoolIterator2Extensions()
2727
}
2828
}
2929

30-
private static unsafe string GetAsciiStringStack(byte* input, int length, StringPool stringPool)
30+
private static unsafe string GetAsciiStringStack(byte* input, int length, IStringCache stringCache)
3131
{
3232
// avoid declaring other local vars, or doing work with stackalloc
3333
// to prevent the .locals init cil flag , see: https://github.com/dotnet/coreclr/issues/1279
3434
char* output = stackalloc char[length];
3535

36-
return GetAsciiStringImplementation(output, input, length, stringPool);
36+
return GetAsciiStringImplementation(output, input, length, stringCache);
3737
}
38-
private static unsafe string GetAsciiStringImplementation(char* outputStart, byte* input, int length, StringPool stringPool)
38+
private static unsafe string GetAsciiStringImplementation(char* outputStart, byte* input, int length, IStringCache stringCache)
3939
{
4040
var hash = _startHash;
4141

@@ -60,29 +60,29 @@ private static unsafe string GetAsciiStringImplementation(char* outputStart, byt
6060
*(output++) = (char)*(input++);
6161
}
6262

63-
return stringPool.GetString(outputStart, hash, length);
63+
return stringCache?.GetString(outputStart, hash, length) ?? new string(outputStart, 0, length);
6464
}
6565

66-
private static unsafe string GetAsciiStringStack(MemoryPoolBlock2 start, MemoryPoolIterator2 end, int inputOffset, int length, StringPool stringPool)
66+
private static unsafe string GetAsciiStringStack(MemoryPoolBlock2 start, MemoryPoolIterator2 end, int inputOffset, int length, IStringCache stringCache)
6767
{
6868
// avoid declaring other local vars, or doing work with stackalloc
6969
// to prevent the .locals init cil flag , see: https://github.com/dotnet/coreclr/issues/1279
7070
char* output = stackalloc char[length];
7171

72-
return GetAsciiStringImplementation(output, start, end, inputOffset, length, stringPool);
72+
return GetAsciiStringImplementation(output, start, end, inputOffset, length, stringCache);
7373
}
7474

75-
private unsafe static string GetAsciiStringHeap(MemoryPoolBlock2 start, MemoryPoolIterator2 end, int inputOffset, int length, StringPool stringPool)
75+
private unsafe static string GetAsciiStringHeap(MemoryPoolBlock2 start, MemoryPoolIterator2 end, int inputOffset, int length, IStringCache stringCache)
7676
{
7777
var buffer = new char[length];
7878

7979
fixed (char* output = buffer)
8080
{
81-
return GetAsciiStringImplementation(output, start, end, inputOffset, length, stringPool);
81+
return GetAsciiStringImplementation(output, start, end, inputOffset, length, stringCache);
8282
}
8383
}
8484

85-
private static unsafe string GetAsciiStringImplementation(char* outputStart, MemoryPoolBlock2 start, MemoryPoolIterator2 end, int inputOffset, int length, StringPool stringPool)
85+
private static unsafe string GetAsciiStringImplementation(char* outputStart, MemoryPoolBlock2 start, MemoryPoolIterator2 end, int inputOffset, int length, IStringCache stringCache)
8686
{
8787
var hash = _startHash;
8888

@@ -129,10 +129,10 @@ private static unsafe string GetAsciiStringImplementation(char* outputStart, Mem
129129
block = block.Next;
130130
inputOffset = block?.Start??0;
131131
}
132-
return stringPool.GetString(outputStart, hash, length);
132+
return stringCache?.GetString(outputStart, hash, length) ?? new string(outputStart, 0, length);
133133
}
134134

135-
public unsafe static string GetAsciiString(this MemoryPoolIterator2 start, MemoryPoolIterator2 end, StringPool stringPool)
135+
public unsafe static string GetAsciiString(this MemoryPoolIterator2 start, MemoryPoolIterator2 end, IStringCache stringCache)
136136
{
137137
if (start.IsDefault || end.IsDefault)
138138
{
@@ -153,16 +153,16 @@ public unsafe static string GetAsciiString(this MemoryPoolIterator2 start, Memor
153153
{
154154
fixed (byte* input = start.Block.Array)
155155
{
156-
return GetAsciiStringStack(input + start.Index, length, stringPool);
156+
return GetAsciiStringStack(input + start.Index, length, stringCache);
157157
}
158158
}
159159

160160
if (length > _maxStackAllocBytes)
161161
{
162-
return GetAsciiStringHeap(start.Block, end, start.Index, length, stringPool);
162+
return GetAsciiStringHeap(start.Block, end, start.Index, length, stringCache);
163163
}
164164

165-
return GetAsciiStringStack(start.Block, end, start.Index, length, stringPool);
165+
return GetAsciiStringStack(start.Block, end, start.Index, length, stringCache);
166166
}
167167

168168
public static string GetUtf8String(this MemoryPoolIterator2 start, MemoryPoolIterator2 end)

src/Microsoft.AspNet.Server.Kestrel/Infrastructure/StringPool.cs renamed to src/Microsoft.AspNet.Server.Kestrel/Infrastructure/StringCache.cs

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,33 @@
55

66
namespace Microsoft.AspNet.Server.Kestrel.Infrastructure
77
{
8-
public class StringPool
8+
public class StringCache : IStringCache
99
{
10+
private int _maxCached;
11+
private int _maxCachedStringLength;
12+
13+
private readonly uint[] _hashes;
14+
private readonly int[] _lastUse;
15+
private readonly string[] _strings;
16+
17+
private int _currentUse = 0;
18+
1019
// x64 int array byte size (28 + length * 4) rounded up to 8 bytes
1120
// x86 int array byte size (12 + length * 4) rounded up to 4 bytes
1221
// Array of 25 ints is 2 consecutive cache lines on x64; second prefetched
1322
// Array of 9 ints is 1 cache line on x64
14-
private const int _maxCached = 25;
15-
private const int _maxCacheLength = 256;
16-
17-
private readonly uint[] _hashes = new uint[_maxCached];
18-
private readonly int[] _lastUse = new int[_maxCached];
19-
private readonly string[] _strings = new string[_maxCached];
23+
public StringCache() : this(25, 256)
24+
{
25+
}
2026

21-
private int _currentUse = 0;
27+
public StringCache(int maxCached, int maxCachedStringLength)
28+
{
29+
_maxCached = maxCached;
30+
_maxCachedStringLength = maxCachedStringLength;
31+
_hashes = new uint[maxCached];
32+
_lastUse = new int[maxCached];
33+
_strings = new string[maxCached];
34+
}
2235

2336
public void MarkStart()
2437
{
@@ -27,7 +40,7 @@ public void MarkStart()
2740

2841
public unsafe string GetString(char* data, uint hash, int length)
2942
{
30-
if (length > _maxCacheLength)
43+
if (length > _maxCachedStringLength)
3144
{
3245
return new string(data, 0, length);
3346
}
@@ -50,7 +63,7 @@ public unsafe string GetString(char* data, uint hash, int length)
5063
if (cachedString.Length != length)
5164
{
5265
#if DEBUG
53-
Console.WriteLine($"{nameof(StringPool)} Collision differing lengths {cachedString.Length} and {length}");
66+
Console.WriteLine($"{nameof(StringCache)} Collision differing lengths {cachedString.Length} and {length}");
5467
#endif
5568
continue;
5669
}
@@ -72,7 +85,7 @@ public unsafe string GetString(char* data, uint hash, int length)
7285
)
7386
{
7487
#if DEBUG
75-
Console.WriteLine($"{nameof(StringPool)} Collision same length, differing strings");
88+
Console.WriteLine($"{nameof(StringCache)} Collision same length, differing strings");
7689
#endif
7790
continue;
7891
}
@@ -84,7 +97,7 @@ public unsafe string GetString(char* data, uint hash, int length)
8497
if (*(cached++) != *(potential++))
8598
{
8699
#if DEBUG
87-
Console.WriteLine($"{nameof(StringPool)} Collision same length, differing strings");
100+
Console.WriteLine($"{nameof(StringCache)} Collision same length, differing strings");
88101
#endif
89102
continue;
90103
}
@@ -101,8 +114,8 @@ public unsafe string GetString(char* data, uint hash, int length)
101114
#if DEBUG
102115
if (_lastUse[oldestIndex] != 0)
103116
{
104-
Console.WriteLine($"{nameof(StringPool)} Evict: {_strings[oldestIndex]} {_lastUse[oldestIndex]} {_hashes[oldestIndex]}");
105-
Console.WriteLine($"{nameof(StringPool)} New: {value} {_currentUse} {hash}");
117+
Console.WriteLine($"{nameof(StringCache)} Evict: {_strings[oldestIndex]} {_lastUse[oldestIndex]} {_hashes[oldestIndex]}");
118+
Console.WriteLine($"{nameof(StringCache)} New: {value} {_currentUse} {hash}");
106119
}
107120
#endif
108121
_lastUse[oldestIndex] = _currentUse;

test/Microsoft.AspNet.Server.KestrelTests/AsciiDecoder.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class AsciiDecoderTests
1313
[Fact]
1414
private void FullByteRangeSupported()
1515
{
16-
var stringPool = new StringPool();
16+
var stringCache = new StringCache();
1717
var byteRange = Enumerable.Range(0, 255).Select(x => (byte)x).ToArray();
1818

1919
var mem = MemoryPoolBlock2.Create(new ArraySegment<byte>(byteRange), IntPtr.Zero, null, null);
@@ -22,7 +22,7 @@ private void FullByteRangeSupported()
2222
var begin = mem.GetIterator();
2323
var end = GetIterator(begin, byteRange.Length);
2424

25-
var s = begin.GetAsciiString(end, stringPool);
25+
var s = begin.GetAsciiString(end, stringCache);
2626

2727
Assert.Equal(s.Length, byteRange.Length);
2828

@@ -38,7 +38,7 @@ private void FullByteRangeSupported()
3838
[Fact]
3939
private void MultiBlockProducesCorrectResults()
4040
{
41-
var stringPool = new StringPool();
41+
var stringCache = new StringCache();
4242
var byteRange = Enumerable.Range(0, 512 + 64).Select(x => (byte)x).ToArray();
4343
var expectedByteRange = byteRange
4444
.Concat(byteRange)
@@ -62,7 +62,7 @@ private void MultiBlockProducesCorrectResults()
6262
var begin = mem0.GetIterator();
6363
var end = GetIterator(begin, expectedByteRange.Length);
6464

65-
var s = begin.GetAsciiString(end, stringPool);
65+
var s = begin.GetAsciiString(end, stringCache);
6666

6767
Assert.Equal(s.Length, expectedByteRange.Length);
6868

@@ -78,7 +78,7 @@ private void MultiBlockProducesCorrectResults()
7878
[Fact]
7979
private void HeapAllocationProducesCorrectResults()
8080
{
81-
var stringPool = new StringPool();
81+
var stringCache = new StringCache();
8282
var byteRange = Enumerable.Range(0, 16384 + 64).Select(x => (byte)x).ToArray();
8383
var expectedByteRange = byteRange.Concat(byteRange).ToArray();
8484

@@ -92,7 +92,7 @@ private void HeapAllocationProducesCorrectResults()
9292
var begin = mem0.GetIterator();
9393
var end = GetIterator(begin, expectedByteRange.Length);
9494

95-
var s = begin.GetAsciiString(end, stringPool);
95+
var s = begin.GetAsciiString(end, stringCache);
9696

9797
Assert.Equal(s.Length, expectedByteRange.Length);
9898

test/Microsoft.AspNet.Server.KestrelTests/FrameTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public void ChunkedPrefixMustBeHexCrLfWithoutLeadingZeros(int dataCount, string
5050
public void EmptyHeaderValuesCanBeParsed(string rawHeaders, int numHeaders)
5151
{
5252
var trace = new KestrelTrace(new TestKestrelTrace());
53-
var stringPool = new StringPool();
53+
var stringCache = new StringCache();
5454
var ltp = new LoggingThreadPool(trace);
5555
var socketInput = new SocketInput(new MemoryPool2(), ltp);
5656
var headerCollection = new FrameRequestHeaders();
@@ -60,7 +60,7 @@ public void EmptyHeaderValuesCanBeParsed(string rawHeaders, int numHeaders)
6060
Buffer.BlockCopy(headerArray, 0, inputBuffer.Data.Array, inputBuffer.Data.Offset, headerArray.Length);
6161
socketInput.IncomingComplete(headerArray.Length, null);
6262

63-
var success = Frame.TakeMessageHeaders(socketInput, headerCollection, stringPool);
63+
var success = Frame.TakeMessageHeaders(socketInput, headerCollection, stringCache);
6464

6565
Assert.True(success);
6666
Assert.Equal(numHeaders, headerCollection.Count());

0 commit comments

Comments
 (0)