add stringbuilder pool for tokenizers

we could replace these with spans in the next net core however for now our pools seem to increase performance by reducing gc load.
This commit is contained in:
Eliot Jones
2020-04-04 18:31:55 +01:00
parent cf46230c05
commit 7baa18b5dd
4 changed files with 77 additions and 4 deletions

View File

@@ -8,6 +8,8 @@
internal class NumericTokenizer : ITokenizer
{
private static readonly StringBuilderPool StringBuilderPool = new StringBuilderPool(10);
private const byte Zero = 48;
private const byte Nine = 57;
@@ -21,7 +23,7 @@
if ((currentByte >= Zero && currentByte <= Nine) || currentByte == '-' || currentByte == '+' || currentByte == '.')
{
characters = new StringBuilder();
characters = StringBuilderPool.Borrow();
characters.Append((char)currentByte);
}
else
@@ -51,6 +53,7 @@
try
{
var str = characters.ToString();
StringBuilderPool.Return(characters);
switch (str)
{

View File

@@ -1,11 +1,12 @@
namespace UglyToad.PdfPig.Tokenization
{
using System.Text;
using Core;
using Tokens;
internal class PlainTokenizer : ITokenizer
{
private static readonly StringBuilderPool StringBuilderPool = new StringBuilderPool(10);
public bool ReadsNextByte { get; } = true;
public bool TryTokenize(byte currentByte, IInputBytes inputBytes, out IToken token)
@@ -17,7 +18,7 @@
return false;
}
var builder = new StringBuilder();
var builder = StringBuilderPool.Borrow();
builder.Append((char)currentByte);
while (inputBytes.MoveNext())
{
@@ -38,6 +39,7 @@
}
var text = builder.ToString();
StringBuilderPool.Return(builder);
switch (text)
{

View File

@@ -0,0 +1,65 @@
namespace UglyToad.PdfPig.Tokenization
{
using System.Collections.Generic;
using System.Text;
/// <summary>
/// A pool for <see cref="StringBuilder"/>s to reduce allocations during tokenization.
/// </summary>
public class StringBuilderPool
{
private readonly int capacity;
private readonly object locker = new object();
private readonly Stack<StringBuilder> pool = new Stack<StringBuilder>();
/// <summary>
/// Create a new <see cref="StringBuilderPool"/> holding the number of items specified by the capacity.
/// </summary>
public StringBuilderPool(int capacity = 5)
{
this.capacity = capacity;
for (var i = 0; i < capacity; i++)
{
pool.Push(new StringBuilder());
}
}
/// <summary>
/// Get an item from the pool, remember to return it using <see cref="Return"/> at the end.
/// </summary>
public StringBuilder Borrow()
{
lock (locker)
{
if (pool.Count == 0)
{
return new StringBuilder();
}
return pool.Pop();
}
}
/// <summary>
/// Returns an item to the pool of available builders.
/// </summary>
public void Return(StringBuilder instance)
{
if (instance == null)
{
return;
}
instance.Clear();
lock (locker)
{
if (pool.Count < capacity)
{
pool.Push(instance);
}
}
}
}
}

View File

@@ -6,11 +6,11 @@
internal class StringTokenizer : ITokenizer
{
private static readonly StringBuilderPool StringBuilderPool = new StringBuilderPool(16);
public bool ReadsNextByte { get; } = false;
public bool TryTokenize(byte currentByte, IInputBytes inputBytes, out IToken token)
{
var builder = new StringBuilder();
token = null;
if (inputBytes == null)
@@ -23,6 +23,7 @@
return false;
}
var builder = StringBuilderPool.Borrow();
var numberOfBrackets = 1;
var isEscapeActive = false;
var isLineBreaking = false;
@@ -177,6 +178,8 @@
encodedWith = StringToken.Encoding.Iso88591;
}
StringBuilderPool.Return(builder);
token = new StringToken(tokenStr, encodedWith);
return true;