Cache StringLine[]s in StringLineGroup with a custom ArrayPool

This commit is contained in:
MihaZupan
2019-10-14 13:04:27 +02:00
parent 76c3e88c58
commit aefad219cf
6 changed files with 123 additions and 16 deletions

View File

@@ -0,0 +1,86 @@
using System.Threading;
namespace Markdig.Helpers
{
internal sealed class CustomArrayPool<T>
{
private sealed class Bucket
{
private readonly T[][] _buffers;
private int _index;
private int _lock;
public Bucket(int numberOfBuffers)
{
_buffers = new T[numberOfBuffers][];
}
public T[] Rent()
{
T[][] buffers = _buffers;
T[] buffer = null;
if (Interlocked.CompareExchange(ref _lock, 1, 0) == 0)
{
int index = _index;
if ((uint)index < (uint)buffers.Length)
{
buffer = buffers[index];
buffers[index] = null;
_index = index + 1;
}
Interlocked.Decrement(ref _lock);
}
return buffer;
}
public void Return(T[] array)
{
var buffers = _buffers;
if (Interlocked.CompareExchange(ref _lock, 1, 0) == 0)
{
int index = _index - 1;
if ((uint)index < (uint)buffers.Length)
{
buffers[index] = array;
_index = index;
}
Interlocked.Decrement(ref _lock);
}
}
}
private readonly Bucket _bucket4, _bucket8, _bucket16, _bucket32;
public CustomArrayPool(int size4, int size8, int size16, int size32)
{
_bucket4 = new Bucket(size4);
_bucket8 = new Bucket(size8);
_bucket16 = new Bucket(size16);
_bucket32 = new Bucket(size32);
}
private Bucket SelectBucket(int length)
{
switch (length)
{
case 4: return _bucket4;
case 8: return _bucket8;
case 16: return _bucket16;
case 32: return _bucket32;
default: return null;
}
}
public T[] Rent(int length)
{
return SelectBucket(length)?.Rent() ?? new T[length];
}
public void Return(T[] array)
{
SelectBucket(array.Length)?.Return(array);
}
}
}

View File

@@ -6,7 +6,7 @@ using System.Text;
namespace Markdig.Helpers
{
/// <summary>
/// Extensions for StringBuilder with <see cref="StringSlice"/>
/// Extensions for StringBuilder
/// </summary>
public static class StringBuilderExtensions
{
@@ -19,5 +19,12 @@ namespace Markdig.Helpers
{
return builder.Append(slice.Text, slice.Start, slice.Length);
}
internal static string GetStringAndReset(this StringBuilder builder)
{
string text = builder.ToString();
builder.Length = 0;
return text;
}
}
}

View File

@@ -5,26 +5,28 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using Markdig.Extensions.Tables;
namespace Markdig.Helpers
{
/// <summary>
/// A group of <see cref="StringLine"/>.
/// </summary>
/// <seealso cref="System.Collections.IEnumerable" />
/// <seealso cref="IEnumerable" />
public struct StringLineGroup : IEnumerable
{
// Feel free to change these numbers if you see a positive change
private static readonly CustomArrayPool<StringLine> _pool
= new CustomArrayPool<StringLine>(512, 386, 128, 64);
/// <summary>
/// Initializes a new instance of the <see cref="StringLineGroup"/> class.
/// </summary>
/// <param name="capacity"></param>
public StringLineGroup(int capacity)
public StringLineGroup(int capacity, bool willRelease = false)
{
if (capacity <= 0) throw new ArgumentOutOfRangeException(nameof(capacity));
Lines = new StringLine[capacity];
Lines = _pool.Rent(willRelease ? Math.Max(8, capacity) : capacity);
Count = 0;
}
@@ -183,14 +185,24 @@ namespace Markdig.Helpers
private void IncreaseCapacity()
{
var newItems = new StringLine[Lines.Length * 2];
var newItems = _pool.Rent(Lines.Length * 2);
if (Count > 0)
{
Array.Copy(Lines, 0, newItems, 0, Count);
Array.Clear(Lines, 0, Count);
}
_pool.Return(Lines);
Lines = newItems;
}
internal void Release()
{
Array.Clear(Lines, 0, Count);
_pool.Return(Lines);
Lines = null;
Count = -1;
}
/// <summary>
/// The iterator used to iterate other the lines.
/// </summary>
@@ -207,7 +219,7 @@ namespace Markdig.Helpers
_offset = -1;
SliceIndex = 0;
CurrentChar = '\0';
End = -2;
End = -2;
for (int i = 0; i < lines.Count; i++)
{
End += lines.Lines[i].Slice.Length + 1; // Add chars

View File

@@ -500,6 +500,11 @@ namespace Markdig.Parsers
if (!block.Parser.Close(this, block))
{
block.Parent?.Remove(block);
if (block is LeafBlock leaf)
{
leaf.Lines.Release();
}
}
else
{

View File

@@ -192,7 +192,7 @@ namespace Markdig.Parsers
previousLineIndexForSliceOffset = 0;
lineOffsets.Clear();
var text = leafBlock.Lines.ToSlice(lineOffsets);
leafBlock.Lines = new StringLineGroup();
leafBlock.Lines.Release();
int previousStart = -1;

View File

@@ -1,4 +1,4 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System.Diagnostics;
@@ -51,7 +51,7 @@ namespace Markdig.Syntax
{
if (Lines.Lines == null)
{
Lines = new StringLineGroup(4);
Lines = new StringLineGroup(4, ProcessInlines);
}
var stringLine = new StringLine(ref slice, line, column, sourceLinePosition);
@@ -64,12 +64,9 @@ namespace Markdig.Syntax
{
// We need to expand tabs to spaces
var builder = StringBuilderCache.Local();
for (int i = column; i < CharHelper.AddTab(column); i++)
{
builder.Append(' ');
}
builder.Append(' ', CharHelper.AddTab(column) - column);
builder.Append(slice.Text, slice.Start + 1, slice.Length - 1);
stringLine.Slice = new StringSlice(builder.ToString());
stringLine.Slice = new StringSlice(builder.GetStringAndReset());
Lines.Add(ref stringLine);
}
}