Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP. More Efficient MemoryAllocator #1660

Closed
wants to merge 30 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
8da4092
Add GC handling, use shared, simplify pool
JimBobSquarePants Jun 12, 2021
1a0ce6f
Only assign reference if using the large pool
JimBobSquarePants Jun 12, 2021
407092b
Fix null ref
JimBobSquarePants Jun 12, 2021
ad96db7
Introduce a timer that performs a callback to cleanup
JimBobSquarePants Jun 12, 2021
6306567
Pass allocator directly
JimBobSquarePants Jun 13, 2021
a9ee629
Update ArrayPoolMemoryAllocator.Buffer{T}.cs
JimBobSquarePants Jun 13, 2021
6036a39
Use GC Aware configurable pool.
JimBobSquarePants Jun 13, 2021
c860447
Simplify and use 2MB buffers
JimBobSquarePants Jun 13, 2021
e9105d3
Fix iterator tests
JimBobSquarePants Jun 13, 2021
4feb673
Add unmanaged buffer implementation
JimBobSquarePants Jun 14, 2021
0de66a4
Cleanup and fix warnings
JimBobSquarePants Jun 14, 2021
f9395fd
Fix everything but Tiff
JimBobSquarePants Jun 14, 2021
5e51795
Merge branch 'master' into js/memory-experiments
JimBobSquarePants Jun 14, 2021
801e887
Merge branch 'master' into js/memory-experiments
JimBobSquarePants Jun 14, 2021
06f9557
Merge branch 'master' into js/memory-experiments
JimBobSquarePants Jun 14, 2021
0aa3211
Better naming, fix last failing test
JimBobSquarePants Jun 14, 2021
73bd60a
Remove factory methods
JimBobSquarePants Jun 14, 2021
f77923a
Merge branch 'master' into js/memory-experiments
JimBobSquarePants Jun 15, 2021
ce668dd
Clean up utilities and namespaces.
JimBobSquarePants Jun 15, 2021
65d2a28
Remove IManagedByteBuffer
JimBobSquarePants Jun 15, 2021
88adace
Fix all but 2 bitmap decode tests
JimBobSquarePants Jun 15, 2021
9c51119
Fix bmp tests, re-enable dither tests
JimBobSquarePants Jun 15, 2021
4c4f9d3
Merge branch 'master' into js/memory-experiments
JimBobSquarePants Jun 16, 2021
0e01d1b
Skip failing test on non windows
JimBobSquarePants Jun 16, 2021
b18c871
Skip another assertation on UNIX
JimBobSquarePants Jun 16, 2021
e9e1948
Update ImageSharp.sln
JimBobSquarePants Jun 16, 2021
65c67d3
Smarter pixel map reuse.
JimBobSquarePants Jun 16, 2021
a22b0cb
Skip flaky test
JimBobSquarePants Jun 16, 2021
f01bc0a
Fix first ticks.
JimBobSquarePants Jun 17, 2021
547614a
Skip flaky test
JimBobSquarePants Jun 17, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,39 @@
<Optimize>true</Optimize>
</PropertyGroup>

<Choose>
<When Condition="'$(TargetFramework)' == 'net472' OR '$(TargetFramework)' == 'net48'">
<PropertyGroup>
<DefineConstants>$(DefineConstants);SUPPORTS_CRITICALFINALIZER</DefineConstants>
</PropertyGroup>
</When>
<When Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PropertyGroup>
<DefineConstants>$(DefineConstants);SUPPORTS_CRITICALFINALIZER</DefineConstants>
</PropertyGroup>
</When>
<When Condition="'$(TargetFramework)' == 'netstandard2.1'">
<PropertyGroup>
<DefineConstants>$(DefineConstants);SUPPORTS_CRITICALFINALIZER</DefineConstants>
</PropertyGroup>
</When>
<When Condition="'$(TargetFramework)' == 'netcoreapp2.0'">
<PropertyGroup>
<DefineConstants>$(DefineConstants);SUPPORTS_CRITICALFINALIZER</DefineConstants>
</PropertyGroup>
</When>
<When Condition="'$(TargetFramework)' == 'netcoreapp2.1'">
<PropertyGroup>
<DefineConstants>$(DefineConstants);SUPPORTS_CRITICALFINALIZER</DefineConstants>
</PropertyGroup>
</When>
<When Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)','netcoreapp3.1'))">
<!--NETCORE 3.1. NET5.0, and future versions will fallback to this as the closest target.-->
<PropertyGroup>
<DefineConstants>$(DefineConstants);SUPPORTS_CRITICALFINALIZER</DefineConstants>
<DefineConstants>$(DefineConstants);SUPPORTS_GC_MEMORYINFO</DefineConstants>
</PropertyGroup>
</When>
</Choose>

</Project>
7 changes: 0 additions & 7 deletions src/ImageSharp/Common/Extensions/StreamExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System;
using System.Buffers;
using System.IO;
using SixLabors.ImageSharp.Memory;

namespace SixLabors.ImageSharp
{
Expand Down Expand Up @@ -72,12 +71,6 @@ public static void Skip(this Stream stream, int count)
}
}

public static void Read(this Stream stream, IManagedByteBuffer buffer)
=> stream.Read(buffer.Array, 0, buffer.Length());

public static void Write(this Stream stream, IManagedByteBuffer buffer)
=> stream.Write(buffer.Array, 0, buffer.Length());

#if !SUPPORTS_SPAN_STREAM
// This is a port of the CoreFX implementation and is MIT Licensed:
// https://github.com/dotnet/corefx/blob/17300169760c61a90cab8d913636c1058a30a8c1/src/Common/src/CoreLib/System/IO/Stream.cs#L742
Expand Down
12 changes: 6 additions & 6 deletions src/ImageSharp/Common/Helpers/Numerics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -841,14 +841,12 @@ public static int EvenReduceSum(Vector256<int> accumulator)
/// Note that by convention, input value 0 returns 0 since Log(0) is undefined.
/// </summary>
/// <param name="value">The value.</param>
public static int Log2(uint value)
{
public static int Log2(uint value) =>
#if SUPPORTS_BITOPERATIONS
return BitOperations.Log2(value);
BitOperations.Log2(value);
#else
return Log2SoftwareFallback(value);
Log2SoftwareFallback(value);
#endif
}

#if !SUPPORTS_BITOPERATIONS
/// <summary>
Expand All @@ -874,9 +872,11 @@ private static int Log2SoftwareFallback(uint value)
value |= value >> 16;

// uint.MaxValue >> 27 is always in range [0 - 31] so we use Unsafe.AddByteOffset to avoid bounds check
// - Using deBruijn sequence, k=2, n=5 (2^5=32) : 0b_0000_0111_1100_0100_1010_1100_1101_1101u
// - uint|long -> IntPtr cast on 32-bit platforms does expensive overflow checks not needed here
return Unsafe.AddByteOffset(
ref MemoryMarshal.GetReference(Log2DeBruijn),
(IntPtr)(int)((value * 0x07C4ACDDu) >> 27)); // uint|long -> IntPtr cast on 32-bit platforms does expensive overflow checks not needed here
(IntPtr)(int)((value * 0x07C4ACDDu) >> 27));
}
#endif
}
Expand Down
2 changes: 1 addition & 1 deletion src/ImageSharp/Compression/Zlib/Deflater.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ public void SetLevel(int level)
/// The number of compressed bytes added to the output, or 0 if either
/// <see cref="IsNeedingInput"/> or <see cref="IsFinished"/> returns true or length is zero.
/// </returns>
public int Deflate(byte[] output, int offset, int length)
public int Deflate(Span<byte> output, int offset, int length)
{
int origLength = length;

Expand Down
29 changes: 15 additions & 14 deletions src/ImageSharp/Compression/Zlib/DeflaterEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,9 @@ internal sealed unsafe class DeflaterEngine : IDisposable
/// This array contains the part of the uncompressed stream that
/// is of relevance. The current character is indexed by strstart.
/// </summary>
private IManagedByteBuffer windowMemoryOwner;
private IMemoryOwner<byte> windowMemoryOwner;
private MemoryHandle windowMemoryHandle;
private readonly byte[] window;
private readonly Memory<byte> window;
private readonly byte* pinnedWindowPointer;

private int maxChain;
Expand All @@ -153,8 +153,8 @@ public DeflaterEngine(MemoryAllocator memoryAllocator, DeflateStrategy strategy)

// Create pinned pointers to the various buffers to allow indexing
// without bounds checks.
this.windowMemoryOwner = memoryAllocator.AllocateManagedByteBuffer(2 * DeflaterConstants.WSIZE);
this.window = this.windowMemoryOwner.Array;
this.windowMemoryOwner = memoryAllocator.Allocate<byte>(2 * DeflaterConstants.WSIZE);
this.window = this.windowMemoryOwner.Memory;
this.windowMemoryHandle = this.windowMemoryOwner.Memory.Pin();
this.pinnedWindowPointer = (byte*)this.windowMemoryHandle.Pointer;

Expand Down Expand Up @@ -303,7 +303,7 @@ public void SetLevel(int level)
case DeflaterConstants.DEFLATE_STORED:
if (this.strstart > this.blockStart)
{
this.huffman.FlushStoredBlock(this.window, this.blockStart, this.strstart - this.blockStart, false);
this.huffman.FlushStoredBlock(this.window.Span, this.blockStart, this.strstart - this.blockStart, false);
this.blockStart = this.strstart;
}

Expand All @@ -313,7 +313,7 @@ public void SetLevel(int level)
case DeflaterConstants.DEFLATE_FAST:
if (this.strstart > this.blockStart)
{
this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, false);
this.huffman.FlushBlock(this.window.Span, this.blockStart, this.strstart - this.blockStart, false);
this.blockStart = this.strstart;
}

Expand All @@ -327,7 +327,7 @@ public void SetLevel(int level)

if (this.strstart > this.blockStart)
{
this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, false);
this.huffman.FlushBlock(this.window.Span, this.blockStart, this.strstart - this.blockStart, false);
this.blockStart = this.strstart;
}

Expand Down Expand Up @@ -362,7 +362,8 @@ public void FillWindow()
more = this.inputEnd - this.inputOff;
}

Buffer.BlockCopy(this.inputBuf, this.inputOff, this.window, this.strstart + this.lookahead, more);
// Buffer.BlockCopy(this.inputBuf, this.inputOff, this.window, this.strstart + this.lookahead, more);
Unsafe.CopyBlockUnaligned(ref this.window.Span[this.strstart + this.lookahead], ref this.inputBuf[this.inputOff], (uint)more);

this.inputOff += more;
this.lookahead += more;
Expand Down Expand Up @@ -426,7 +427,7 @@ private int InsertString()

private void SlideWindow()
{
Unsafe.CopyBlockUnaligned(ref this.window[0], ref this.window[DeflaterConstants.WSIZE], DeflaterConstants.WSIZE);
Unsafe.CopyBlockUnaligned(ref this.window.Span[0], ref this.window.Span[DeflaterConstants.WSIZE], DeflaterConstants.WSIZE);
this.matchStart -= DeflaterConstants.WSIZE;
this.strstart -= DeflaterConstants.WSIZE;
this.blockStart -= DeflaterConstants.WSIZE;
Expand Down Expand Up @@ -663,7 +664,7 @@ private bool DeflateStored(bool flush, bool finish)
lastBlock = false;
}

this.huffman.FlushStoredBlock(this.window, this.blockStart, storedLength, lastBlock);
this.huffman.FlushStoredBlock(this.window.Span, this.blockStart, storedLength, lastBlock);
this.blockStart += storedLength;
return !(lastBlock || storedLength == 0);
}
Expand All @@ -683,7 +684,7 @@ private bool DeflateFast(bool flush, bool finish)
if (this.lookahead == 0)
{
// We are flushing everything
this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, finish);
this.huffman.FlushBlock(this.window.Span, this.blockStart, this.strstart - this.blockStart, finish);
this.blockStart = this.strstart;
return false;
}
Expand Down Expand Up @@ -743,7 +744,7 @@ private bool DeflateFast(bool flush, bool finish)
if (this.huffman.IsFull())
{
bool lastBlock = finish && (this.lookahead == 0);
this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, lastBlock);
this.huffman.FlushBlock(this.window.Span, this.blockStart, this.strstart - this.blockStart, lastBlock);
this.blockStart = this.strstart;
return !lastBlock;
}
Expand Down Expand Up @@ -771,7 +772,7 @@ private bool DeflateSlow(bool flush, bool finish)
this.prevAvailable = false;

// We are flushing everything
this.huffman.FlushBlock(this.window, this.blockStart, this.strstart - this.blockStart, finish);
this.huffman.FlushBlock(this.window.Span, this.blockStart, this.strstart - this.blockStart, finish);
this.blockStart = this.strstart;
return false;
}
Expand Down Expand Up @@ -846,7 +847,7 @@ private bool DeflateSlow(bool flush, bool finish)
}

bool lastBlock = finish && (this.lookahead == 0) && !this.prevAvailable;
this.huffman.FlushBlock(this.window, this.blockStart, len, lastBlock);
this.huffman.FlushBlock(this.window.Span, this.blockStart, len, lastBlock);
this.blockStart += len;
return !lastBlock;
}
Expand Down
12 changes: 6 additions & 6 deletions src/ImageSharp/Compression/Zlib/DeflaterHuffman.cs
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ public void CompressBlock()
/// <param name="storedLength">Count of bytes to write</param>
/// <param name="lastBlock">True if this is the last block</param>
[MethodImpl(InliningOptions.ShortMethod)]
public void FlushStoredBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock)
public void FlushStoredBlock(Span<byte> stored, int storedOffset, int storedLength, bool lastBlock)
{
this.Pending.WriteBits((DeflaterConstants.STORED_BLOCK << 1) + (lastBlock ? 1 : 0), 3);
this.Pending.AlignToByte();
Expand All @@ -256,7 +256,7 @@ public void FlushStoredBlock(byte[] stored, int storedOffset, int storedLength,
/// <param name="storedOffset">Index of first byte to flush</param>
/// <param name="storedLength">Count of bytes to flush</param>
/// <param name="lastBlock">True if this is the last block</param>
public void FlushBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock)
public void FlushBlock(Span<byte> stored, int storedOffset, int storedLength, bool lastBlock)
{
this.literalTree.Frequencies[EofSymbol]++;

Expand Down Expand Up @@ -286,13 +286,13 @@ public void FlushBlock(byte[] stored, int storedOffset, int storedLength, bool l
+ this.extraBits;

int static_len = this.extraBits;
ref byte staticLLengthRef = ref MemoryMarshal.GetReference<byte>(StaticLLength);
ref byte staticLLengthRef = ref MemoryMarshal.GetReference(StaticLLength);
for (int i = 0; i < LiteralNumber; i++)
{
static_len += this.literalTree.Frequencies[i] * Unsafe.Add(ref staticLLengthRef, i);
}

ref byte staticDLengthRef = ref MemoryMarshal.GetReference<byte>(StaticDLength);
ref byte staticDLengthRef = ref MemoryMarshal.GetReference(StaticDLength);
for (int i = 0; i < DistanceNumber; i++)
{
static_len += this.distTree.Frequencies[i] * Unsafe.Add(ref staticDLengthRef, i);
Expand Down Expand Up @@ -484,7 +484,7 @@ private sealed class Tree : IDisposable
private IMemoryOwner<short> frequenciesMemoryOwner;
private MemoryHandle frequenciesMemoryHandle;

private IManagedByteBuffer lengthsMemoryOwner;
private IMemoryOwner<byte> lengthsMemoryOwner;
private MemoryHandle lengthsMemoryHandle;

public Tree(MemoryAllocator memoryAllocator, int elements, int minCodes, int maxLength)
Expand All @@ -498,7 +498,7 @@ public Tree(MemoryAllocator memoryAllocator, int elements, int minCodes, int max
this.frequenciesMemoryHandle = this.frequenciesMemoryOwner.Memory.Pin();
this.Frequencies = (short*)this.frequenciesMemoryHandle.Pointer;

this.lengthsMemoryOwner = memoryAllocator.AllocateManagedByteBuffer(elements);
this.lengthsMemoryOwner = memoryAllocator.Allocate<byte>(elements);
this.lengthsMemoryHandle = this.lengthsMemoryOwner.Memory.Pin();
this.Length = (byte*)this.lengthsMemoryHandle.Pointer;

Expand Down
31 changes: 13 additions & 18 deletions src/ImageSharp/Compression/Zlib/DeflaterOutputStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.

using System;
using System.Buffers;
using System.IO;
using SixLabors.ImageSharp.Memory;

Expand All @@ -14,8 +15,7 @@ namespace SixLabors.ImageSharp.Compression.Zlib
internal sealed class DeflaterOutputStream : Stream
{
private const int BufferLength = 512;
private IManagedByteBuffer memoryOwner;
private readonly byte[] buffer;
private IMemoryOwner<byte> bufferOwner;
private Deflater deflater;
private readonly Stream rawStream;
private bool isDisposed;
Expand All @@ -29,8 +29,7 @@ internal sealed class DeflaterOutputStream : Stream
public DeflaterOutputStream(MemoryAllocator memoryAllocator, Stream rawStream, int compressionLevel)
{
this.rawStream = rawStream;
this.memoryOwner = memoryAllocator.AllocateManagedByteBuffer(BufferLength);
this.buffer = this.memoryOwner.Array;
this.bufferOwner = memoryAllocator.Allocate<byte>(BufferLength);
this.deflater = new Deflater(memoryAllocator, compressionLevel);
}

Expand All @@ -49,15 +48,9 @@ public DeflaterOutputStream(MemoryAllocator memoryAllocator, Stream rawStream, i
/// <inheritdoc/>
public override long Position
{
get
{
return this.rawStream.Position;
}
get => this.rawStream.Position;

set
{
throw new NotSupportedException();
}
set => throw new NotSupportedException();
}

/// <inheritdoc/>
Expand Down Expand Up @@ -91,16 +84,17 @@ public override void Write(byte[] buffer, int offset, int count)

private void Deflate(bool flushing)
{
Span<byte> bufferSpan = this.bufferOwner.GetSpan();
while (flushing || !this.deflater.IsNeedingInput)
{
int deflateCount = this.deflater.Deflate(this.buffer, 0, BufferLength);
int deflateCount = this.deflater.Deflate(bufferSpan, 0, BufferLength);

if (deflateCount <= 0)
{
break;
}

this.rawStream.Write(this.buffer, 0, deflateCount);
this.rawStream.Write(bufferSpan, 0, deflateCount);
}

if (!this.deflater.IsNeedingInput)
Expand All @@ -112,15 +106,16 @@ private void Deflate(bool flushing)
private void Finish()
{
this.deflater.Finish();
Span<byte> bufferSpan = this.bufferOwner.GetSpan();
while (!this.deflater.IsFinished)
{
int len = this.deflater.Deflate(this.buffer, 0, BufferLength);
int len = this.deflater.Deflate(bufferSpan, 0, BufferLength);
if (len <= 0)
{
break;
}

this.rawStream.Write(this.buffer, 0, len);
this.rawStream.Write(bufferSpan, 0, len);
}

if (!this.deflater.IsFinished)
Expand All @@ -140,11 +135,11 @@ protected override void Dispose(bool disposing)
{
this.Finish();
this.deflater.Dispose();
this.memoryOwner.Dispose();
this.bufferOwner.Dispose();
}

this.deflater = null;
this.memoryOwner = null;
this.bufferOwner = null;
this.isDisposed = true;
base.Dispose(disposing);
}
Expand Down
Loading