Skip to content

Commit

Permalink
Rewrite decompressor RefillTagFromScatch method to use refs (#49)
Browse files Browse the repository at this point in the history
Motivation
----------
This is a step towards code that doesn't require pinning which can help
with GC when compression/decompression is run a lot. GC will be able to
move memory even when in the middle of a compression or decompression
run and update the ref pointers.

Modifications
-------------
Rewrite RefillTagFromScratch to use refs, returning a ValueTuple so that
the pointer may be incremented the correct amount by the caller.

Results
-------
.NET 6 shows a per improvement, .NET 7 is neutral. .NET 4.8 shows a
slight regression, I'm assuming due to poor enregistration.

BenchmarkDotNet=v0.13.4, OS=Windows 11 (10.0.22000.1455/21H2) Intel Core
i7-10850H CPU 2.70GHz, 1 CPU, 12 logical and 6 physical cores .NET
SDK=7.0.102
[Host] : .NET 7.0.2 (7.0.222.60605), X64 RyuJIT AVX2
MediumRun-.NET 6.0 : .NET 6.0.13 (6.0.1322.58009), X64 RyuJIT AVX2
MediumRun-.NET 7.0 : .NET 7.0.2 (7.0.222.60605), X64 RyuJIT AVX2
MediumRun-.NET Framework 4.8 : .NET Framework 4.8 (4.8.4515.0), X64
RyuJIT VectorSize=256

IterationCount=15  LaunchCount=2  WarmupCount=10

| Method | Job | Runtime | Mean | Error | StdDev | Median | Ratio |
RatioSD |
|-------- |----------------------------- |-------------------
|----------:|---------:|---------:|----------:|------:|--------:|
| Pointer | MediumRun-.NET 6.0 | .NET 6.0 | 109.06 us | 1.150 us | 1.686
us | 108.60 us | 1.00 | 0.00 |
| Ref | MediumRun-.NET 6.0 | .NET 6.0 | 106.20 us | 0.863 us | 1.265 us
| 106.47 us | 0.97 | 0.02 |
| | | | | | | | | |
| Pointer | MediumRun-.NET 7.0 | .NET 7.0 | 93.34 us | 0.877 us | 1.257
us | 93.13 us | 1.00 | 0.00 |
| Ref | MediumRun-.NET 7.0 | .NET 7.0 | 94.00 us | 1.161 us | 1.738 us |
95.01 us | 1.01 | 0.03 |
| | | | | | | | | |
| Pointer | MediumRun-.NET Framework 4.8 | .NET Framework 4.8 | 106.36
us | 0.357 us | 0.501 us | 106.42 us | 1.00 | 0.00 |
| Ref | MediumRun-.NET Framework 4.8 | .NET Framework 4.8 | 111.34 us |
1.041 us | 1.558 us | 110.50 us | 1.05 | 0.02 |
  • Loading branch information
brantburnett authored Feb 6, 2023
1 parent 130b5ba commit 8a631ce
Showing 1 changed file with 12 additions and 11 deletions.
23 changes: 12 additions & 11 deletions Snappier/Internal/SnappyDecompressor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,10 @@ internal unsafe void DecompressAllTags(ReadOnlySpan<byte> inputSpan)
// and any literal data from the _input buffer

// scratch will be the scratch buffer with only the tag if true is returned
if (!RefillTagFromScratch(ref input, inputEnd, scratch))
(bool sufficientData, uint inputUsed) = RefillTagFromScratch(ref Unsafe.AsRef<byte>(input),
ref Unsafe.AsRef<byte>(inputEnd), ref Unsafe.AsRef<byte>(scratch));
input += inputUsed;
if (!sufficientData)
{
return;
}
Expand Down Expand Up @@ -416,33 +419,31 @@ internal unsafe void DecompressAllTags(ReadOnlySpan<byte> inputSpan)
}
}

private unsafe bool RefillTagFromScratch(ref byte* input, byte* inputEnd, byte* scratch)
private (bool sufficientData, uint inputUsed) RefillTagFromScratch(ref byte input, ref byte inputEnd, ref byte scratch)
{
Debug.Assert(_scratchLength > 0);

if (input >= inputEnd)
if (!Unsafe.IsAddressLessThan(ref input, ref inputEnd))
{
return false;
return (false, 0);
}

// Read the tag character
byte c = *scratch;
uint entry = Constants.CharTable[c];
uint entry = Constants.CharTable[scratch];
uint needed = (entry >> 11) + 1; // +1 byte for 'c'

uint toCopy = Math.Min(unchecked((uint)(inputEnd - input)), needed - _scratchLength);
Unsafe.CopyBlockUnaligned(scratch + _scratchLength, input, toCopy);
uint toCopy = Math.Min((uint)Unsafe.ByteOffset(ref input, ref inputEnd), needed - _scratchLength);
Unsafe.CopyBlockUnaligned(ref Unsafe.Add(ref scratch, _scratchLength), ref input, toCopy);

_scratchLength += toCopy;
input += toCopy;

if (_scratchLength < needed)
{
// Still insufficient
return false;
return (false, toCopy);
}

return true;
return (true, toCopy);
}

private unsafe bool RefillTag(ref byte* input, ref byte* inputEnd, byte* scratch)
Expand Down

0 comments on commit 8a631ce

Please # to comment.