Skip to content

Commit

Permalink
Merge pull request #67 from hadashiA/ku/static
Browse files Browse the repository at this point in the history
[perf] Use static cache instead of ArrayPool
  • Loading branch information
hadashiA authored Dec 6, 2023
2 parents ec5598b + bbb87a4 commit 1301b79
Show file tree
Hide file tree
Showing 31 changed files with 493 additions and 481 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ YamlSerializer.Deserialize<T>(yaml, options);
Basic example:

```csharp
using var parser = YamlParser.FromBytes(utf8Bytes);
var parser = YamlParser.FromBytes(utf8Bytes);
// YAML contains more than one `Document`.
// Here we skip to before first document content.
Expand Down Expand Up @@ -555,7 +555,7 @@ while (parser.Read())
parser.Read(); // Skip MappingStart

// Read to end of mapping
while (!parser.End && parser.CurrentEventType != ParseEventType.MappingEnd)
while (parser.CurrentEventType != ParseEventType.MappingEnd)
{
// After Mapping start, key and value appear alternately.

Expand Down Expand Up @@ -590,7 +590,7 @@ Basic usage:

``` csharp
var buffer = new ArrayBufferWriter();
using var emitter = new Utf8YamlEmitter(buffer); // It needs buffer implemented `IBufferWriter<byte>`
var emitter = new Utf8YamlEmitter(buffer); // It needs buffer implemented `IBufferWriter<byte>`
emitter.BeginMapping(); // Mapping is a collection like Dictionary in YAML
{
Expand Down
2 changes: 1 addition & 1 deletion VYaml.Benchmark/SimpleParsingBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public void VYaml_Parser()
{
// for (var i = 0; i < N; i++)
{
using var parser = YamlParser.FromBytes(yamlBytes!);
var parser = YamlParser.FromBytes(yamlBytes!);
while (parser.Read())
{
}
Expand Down
2 changes: 1 addition & 1 deletion VYaml.Benchmark/VYaml.Benchmark.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
Expand Down
41 changes: 26 additions & 15 deletions VYaml.Core/Emitter/Utf8YamlEmitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@ public ref struct Utf8YamlEmitter
static readonly byte[] MappingKeyFooter = { (byte)':', (byte)' ' };
static readonly byte[] FlowMappingEmpty = { (byte)'{', (byte)'}' };

[ThreadStatic]
static ExpandBuffer<char>? stringBufferStatic;

[ThreadStatic]
static ExpandBuffer<EmitState>? stateBufferStatic;

[ThreadStatic]
static ExpandBuffer<int>? elementCountBufferSTatic;

[ThreadStatic]
static ExpandBuffer<string>? tagBufferStatic;

Check warning on line 52 in VYaml.Core/Emitter/Utf8YamlEmitter.cs

View workflow job for this annotation

GitHub Actions / test-dotnet

The field 'Utf8YamlEmitter.tagBufferStatic' is never used

Check warning on line 52 in VYaml.Core/Emitter/Utf8YamlEmitter.cs

View workflow job for this annotation

GitHub Actions / test-dotnet

The field 'Utf8YamlEmitter.tagBufferStatic' is never used

EmitState CurrentState
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -60,10 +72,10 @@ bool IsFirstElement
readonly IBufferWriter<byte> writer;
readonly YamlEmitOptions options;

ExpandBuffer<char> stringBuffer;
ExpandBuffer<EmitState> stateStack;
ExpandBuffer<int> elementCountStack;
ExpandBuffer<string> tagStack;
readonly ExpandBuffer<char> stringBuffer;
readonly ExpandBuffer<EmitState> stateStack;
readonly ExpandBuffer<int> elementCountStack;
readonly ExpandBuffer<string> tagStack;

int currentIndentLevel;
int currentElementCount;
Expand All @@ -74,9 +86,16 @@ public Utf8YamlEmitter(IBufferWriter<byte> writer, YamlEmitOptions? options = nu
this.options = options ?? YamlEmitOptions.Default;

currentIndentLevel = 0;
stringBuffer = new ExpandBuffer<char>(1024);
stateStack = new ExpandBuffer<EmitState>(16);
elementCountStack = new ExpandBuffer<int>(16);

stringBuffer = stringBufferStatic ??= new ExpandBuffer<char>(1024);
stringBuffer.Clear();

stateStack = stateBufferStatic ??= new ExpandBuffer<EmitState>(16);
stateStack.Clear();

elementCountStack = elementCountBufferSTatic ??= new ExpandBuffer<int>(16);
elementCountStack.Clear();

stateStack.Add(EmitState.None);
currentElementCount = 0;

Expand All @@ -85,14 +104,6 @@ public Utf8YamlEmitter(IBufferWriter<byte> writer, YamlEmitOptions? options = nu

internal readonly IBufferWriter<byte> GetWriter() => writer;

public void Dispose()
{
stringBuffer.Dispose();
stateStack.Dispose();
elementCountStack.Dispose();
tagStack.Dispose();
}

public void BeginSequence(SequenceStyle style = SequenceStyle.Block)
{
switch (style)
Expand Down
25 changes: 8 additions & 17 deletions VYaml.Core/Internal/ExpandBuffer.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
#nullable enable
using System;
using System.Buffers;
using System.Runtime.CompilerServices;

namespace VYaml.Internal
{
ref struct ExpandBuffer<T>
class ExpandBuffer<T>
{
const int MinimumGrow = 4;
const int GrowFactor = 200;

public int Length { get; private set; }
T[] buffer;

public ExpandBuffer(int capacity)
{
buffer = ArrayPool<T>.Shared.Rent(capacity);
// buffer = new T[capacity];
// buffer = ArrayPool<T>.Shared.Rent(capacity);
buffer = new T[capacity];
Length = 0;
}

Expand All @@ -25,15 +25,6 @@ public ref T this[int index]
get => ref buffer[index];
}

public int Length { get; private set; }

public void Dispose()
{
if (Length < 0) return;
ArrayPool<T>.Shared.Return(buffer);
Length = -1;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> AsSpan() => buffer.AsSpan(0, Length);

Expand Down Expand Up @@ -92,10 +83,10 @@ void SetCapacity(int newCapacity)
{
if (buffer.Length >= newCapacity) return;

var newBuffer = ArrayPool<T>.Shared.Rent(newCapacity);
// var newBuffer = new T[newCapacity];
Array.Copy(buffer, 0, newBuffer, 0, Length);
ArrayPool<T>.Shared.Return(buffer);
// var newBuffer = ArrayPool<T>.Shared.Rent(newCapacity);
var newBuffer = new T[newCapacity];
buffer.AsSpan(0, Length).CopyTo(newBuffer);
// ArrayPool<T>.Shared.Return(buffer);
buffer = newBuffer;
}

Expand Down
15 changes: 10 additions & 5 deletions VYaml.Core/Internal/InsertionQueue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,18 @@

namespace VYaml.Internal
{
ref struct InsertionQueue<T>
class InsertionQueue<T>
{
const int MinimumGrow = 4;
const int GrowFactor = 200;

public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
private set;
}

T[] array;
int headIndex;
int tailIndex;
Expand All @@ -20,11 +27,9 @@ public InsertionQueue(int capacity)
headIndex = tailIndex = Count = 0;
}

public int Count
public void Clear()
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
private set;
headIndex = tailIndex = Count = 0;
}

public T Peek()
Expand Down
48 changes: 16 additions & 32 deletions VYaml.Core/Parser/Scalar.cs → VYaml.Core/Internal/Scalar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,35 @@
using System;
using System.Buffers;
using System.Buffers.Text;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using VYaml.Internal;

namespace VYaml.Parser
{
ref struct ScalarPool
class ScalarPool
{
ExpandBuffer<Scalar> queue;
public static readonly ScalarPool Shared = new();

public ScalarPool(int capacity)
{
queue = new ExpandBuffer<Scalar>(capacity);
}
readonly ConcurrentQueue<Scalar> queue = new();

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Scalar Rent()
{
return queue.TryPop(out var scalar)
? scalar
: new Scalar(256);
if (queue.TryDequeue(out var value))
{
return value;
}
return new Scalar(256);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Return(Scalar scalar)
{
scalar.Clear();
queue.Add(scalar);
}

public void Dispose()
{
queue.Dispose();
for (var i = 0; i < queue.Length; i++)
{
queue[i].Dispose();
}
queue.Enqueue(scalar);
}
}

class Scalar : ITokenContent, IDisposable
class Scalar : ITokenContent
{
const int MinimumGrow = 4;
const int GrowFactor = 200;
Expand All @@ -53,12 +42,12 @@ class Scalar : ITokenContent, IDisposable

public Scalar(int capacity)
{
buffer = ArrayPool<byte>.Shared.Rent(capacity);
buffer = new byte[capacity];
}

public Scalar(ReadOnlySpan<byte> content)
{
buffer = ArrayPool<byte>.Shared.Rent(content.Length);
buffer = new byte[content.Length];
Write(content);
}

Expand All @@ -68,6 +57,9 @@ public Scalar(ReadOnlySpan<byte> content)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<byte> AsSpan(int start, int length) => buffer.AsSpan(start, length);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<byte> AsUtf8() => buffer.AsSpan(0, Length);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(byte code)
{
Expand Down Expand Up @@ -125,14 +117,6 @@ public void Clear()
Length = 0;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (Length < 0) return;
ArrayPool<byte>.Shared.Return(buffer);
Length = -1;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override string ToString()
{
Expand Down
Loading

0 comments on commit 1301b79

Please sign in to comment.