Skip to content

Commit

Permalink
Array pooling for InternalBitArray
Browse files Browse the repository at this point in the history
  • Loading branch information
jtmueller committed Dec 31, 2021
1 parent b04c346 commit 3fce2d3
Show file tree
Hide file tree
Showing 16 changed files with 252 additions and 89 deletions.
12 changes: 6 additions & 6 deletions benchmarks/Results.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ Original:

Revised:

| Method | Job | Runtime | Mean | Error | StdDev | Gen 0 | Gen 1 | Allocated |
|--------- |----------- |-------------- |-----------:|---------:|---------:|---------:|--------:|----------:|
| LoadOnly | Job-TRPBVX | .NET 6.0 | 708.2 us | 5.30 us | 4.95 us | 49.8047 | 10.7422 | 231 KB |
| SaveOnly | Job-TRPBVX | .NET 6.0 | 1,619.9 us | 21.20 us | 16.55 us | 312.5000 | 5.8594 | 1,438 KB |
| LoadOnly | Job-SMJWQL | .NET Core 3.1 | 886.8 us | 6.18 us | 5.16 us | 49.8047 | 10.7422 | 231 KB |
| SaveOnly | Job-SMJWQL | .NET Core 3.1 | 1,991.9 us | 39.51 us | 35.02 us | 312.5000 | 3.9063 | 1,438 KB |
| Method | Job | Runtime | Mean | Error | StdDev | Gen 0 | Gen 1 | Allocated |
|--------- |----------- |-------------- |-----------:|--------:|--------:|--------:|--------:|----------:|
| LoadOnly | Job-MHBQCF | .NET 6.0 | 735.6 us | 4.74 us | 4.20 us | 50.7813 | 11.7188 | 237 KB |
| SaveOnly | Job-MHBQCF | .NET 6.0 | 1,223.6 us | 7.57 us | 8.42 us | 44.9219 | - | 215 KB |
| LoadOnly | Job-OTQTAE | .NET Core 3.1 | 880.1 us | 3.63 us | 3.22 us | 50.7813 | 11.7188 | 237 KB |
| SaveOnly | Job-OTQTAE | .NET Core 3.1 | 1,426.6 us | 8.43 us | 7.88 us | 44.9219 | - | 215 KB |
2 changes: 1 addition & 1 deletion src/IO/BitReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,5 +141,5 @@ public string ReadString(int byteCount)

public void Align() => Position = (Position + 7) & ~7;

public void Dispose() => _bits = null!;
public void Dispose() => Interlocked.Exchange(ref _bits!, null)?.Dispose();
}
6 changes: 3 additions & 3 deletions src/IO/BitWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,13 @@ public void WriteBits(IList<bool> bits, int numberOfBits)

public void WriteBytes(ReadOnlySpan<byte> value)
{
var bits = new InternalBitArray(value);
using var bits = new InternalBitArray(value);
WriteBits(bits);
}

public void WriteBytes(ReadOnlySpan<byte> value, int numberOfBits)
{
var bits = new InternalBitArray(value) { Length = numberOfBits };
using var bits = new InternalBitArray(value) { Length = numberOfBits };
WriteBits(bits, numberOfBits);
}

Expand Down Expand Up @@ -174,5 +174,5 @@ private void InternalGetBytes(Span<byte> output)
public void SeekBits(int bitPosition) => Position = bitPosition;
public void Seek(int bytePostion) => SeekBits(bytePostion * 8);
public void Align() => Position = (Position + 7) & ~7;
public void Dispose() => _bits = null!;
public void Dispose() => Interlocked.Exchange(ref _bits!, null)?.Dispose();
}
28 changes: 21 additions & 7 deletions src/IO/InternalBitArray.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
// Adapted from here, to allow for span-based constructor. Removed unused/unsafe code.
// https://github.com/dotnet/runtime/blob/main/src/libraries/System.Collections/src/System/Collections/BitArray.cs

using Microsoft.Toolkit.HighPerformance;
using System.Buffers;
using System.Buffers.Binary;
using System.Collections;
using System.Diagnostics;
Expand All @@ -13,7 +15,7 @@ namespace D2SLib.IO;

// A vector of bits. Use this to store bits efficiently
[Serializable]
internal sealed class InternalBitArray : IList<bool>, ICloneable
internal sealed class InternalBitArray : IList<bool>, ICloneable, IDisposable
{
private int[] m_array; // Do not rename (binary serialization)
private int m_length; // Do not rename (binary serialization)
Expand Down Expand Up @@ -45,18 +47,20 @@ public InternalBitArray(int length, bool defaultValue)
throw new ArgumentOutOfRangeException(nameof(length), length, "Length must be non-negative");
}

m_array = new int[GetInt32ArrayLengthFromBitLength(length)];
int arrayLength = GetInt32ArrayLengthFromBitLength(length);
m_array = ArrayPool<int>.Shared.Rent(arrayLength);
m_length = length;

if (defaultValue)
{
Array.Fill(m_array, -1);
var span = m_array.AsSpan(0, arrayLength);
span.Fill(-1);

// clear high bit values in the last int
Div32Rem(length, out int extraBits);
if (extraBits > 0)
{
m_array[^1] = (1 << extraBits) - 1;
span[^1] = (1 << extraBits) - 1;
}
}

Expand All @@ -81,7 +85,7 @@ public InternalBitArray(ReadOnlySpan<byte> bytes)
throw new ArgumentException("Too many bytes!", nameof(bytes));
}

m_array = new int[GetInt32ArrayLengthFromByteLength(bytes.Length)];
m_array = ArrayPool<int>.Shared.Rent(GetInt32ArrayLengthFromByteLength(bytes.Length));
m_length = bytes.Length * BitsPerByte;

uint totalCount = (uint)bytes.Length / 4;
Expand Down Expand Up @@ -127,7 +131,7 @@ public InternalBitArray(InternalBitArray bits)

int arrayLength = GetInt32ArrayLengthFromBitLength(bits.m_length);

m_array = new int[arrayLength];
m_array = ArrayPool<int>.Shared.Rent(arrayLength);

Debug.Assert(bits.m_array.Length <= arrayLength);

Expand Down Expand Up @@ -237,7 +241,7 @@ public int Length
if (newints > m_array.Length || newints + _ShrinkThreshold < m_array.Length)
{
// grow or shrink (if wasting more than _ShrinkThreshold ints)
Array.Resize(ref m_array, newints);
ArrayPool<int>.Shared.Resize(ref m_array!, newints);
}

if (value > m_length)
Expand Down Expand Up @@ -333,6 +337,16 @@ private static int Div32Rem(int number, out int remainder)
void ICollection<bool>.CopyTo(bool[] array, int arrayIndex) => throw new NotImplementedException();
bool ICollection<bool>.Remove(bool item) => throw new NotImplementedException();

public void Dispose()
{
if (m_array.Length > 0)
{
ArrayPool<int>.Shared.Return(m_array);
m_array = Array.Empty<int>();
m_length = 0;
}
}

private sealed class BitArrayEnumeratorSimple : IEnumerator<bool>, ICloneable
{
private readonly InternalBitArray _bitArray;
Expand Down
2 changes: 1 addition & 1 deletion src/Model/Data/ItemsData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public ItemsData(ArmorData armorData, WeaponsData weaponsData, MiscData miscData
public MiscData MiscData { get; }

private HuffmanTree? _itemCodeTree = null;
public HuffmanTree ItemCodeTree
internal HuffmanTree ItemCodeTree
{
get => _itemCodeTree ??= InitializeHuffmanTree();
set => _itemCodeTree = value;
Expand Down
5 changes: 2 additions & 3 deletions src/Model/Huffman/HuffmanTree.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
using D2SLib.IO;
using System.Collections;

namespace D2SLib.Model.Huffman;

//hardcoded....
public class HuffmanTree
internal class HuffmanTree
{
public Node? Root { get; set; }

Expand Down Expand Up @@ -79,7 +78,7 @@ public void Build()
}
}

public IEnumerable<bool> EncodeChar(char source)
public InternalBitArray EncodeChar(char source)
{
var encodedSymbol = Root?.Traverse(source, new InternalBitArray(0));
if (encodedSymbol is null)
Expand Down
3 changes: 1 addition & 2 deletions src/Model/Huffman/Node.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace D2SLib.Model.Huffman;

public class Node
internal class Node
{
public char Symbol { get; set; }
public int Frequency { get; set; }
Expand All @@ -17,7 +17,6 @@ public class Node
}
else
{

if (Left is not null)
{
data.Add(false);
Expand Down
17 changes: 14 additions & 3 deletions src/Model/Save/Corpses.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace D2SLib.Model.Save;

public sealed class CorpseList
public sealed class CorpseList : IDisposable
{
public CorpseList(ushort? header, ushort count)
{
Expand Down Expand Up @@ -45,9 +45,18 @@ public static byte[] Write(CorpseList corpseList, uint version)
corpseList.Write(writer, version);
return writer.ToArray();
}

public void Dispose()
{
foreach (var corpse in Corpses)
{
corpse?.Dispose();
}
Corpses.Clear();
}
}

public sealed class Corpse
public sealed class Corpse : IDisposable
{
private Corpse(IBitReader reader, uint version)
{
Expand All @@ -60,7 +69,7 @@ private Corpse(IBitReader reader, uint version)
public uint? Unk0x0 { get; set; }
public uint X { get; set; }
public uint Y { get; set; }
public ItemList ItemList { get; set; }
public ItemList ItemList { get; }

public void Write(IBitWriter writer, uint version)
{
Expand All @@ -83,5 +92,7 @@ public static byte[] Write(Corpse corpse, uint version)
corpse.Write(writer, version);
return writer.ToArray();
}

public void Dispose() => ItemList.Dispose();
}

3 changes: 2 additions & 1 deletion src/Model/Save/D2I.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace D2SLib.Model.Save;

public class D2I
public sealed class D2I : IDisposable
{
private D2I(IBitReader reader, uint version)
{
Expand Down Expand Up @@ -31,4 +31,5 @@ public static byte[] Write(D2I d2i, uint version)
return writer.ToArray();
}

public void Dispose() => ItemList?.Dispose();
}
12 changes: 11 additions & 1 deletion src/Model/Save/D2S.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace D2SLib.Model.Save;

public class D2S
public sealed class D2S : IDisposable
{
private D2S(IBitReader reader)
{
Expand Down Expand Up @@ -188,4 +188,14 @@ public static byte[] Write(D2S d2s)
Header.Fix(bytes);
return bytes;
}

public void Dispose()
{
Waypoints.Dispose();
Status.Dispose();
Quests.Dispose();
PlayerItemList.Dispose();
PlayerCorpses.Dispose();
MercenaryItemList?.Dispose();
}
}
Loading

0 comments on commit 3fce2d3

Please sign in to comment.