Skip to content

Commit

Permalink
Handle null chunk type using enum value
Browse files Browse the repository at this point in the history
Motivation
----------
Trying to narrow down perf loss in .NET 8 compared to .NET 6 specific
to stream decompression.

Modifications
-------------
Rather than using a `Nullable<ChunkType>` to track when the chunk type
is unknown, add an enum value that represents null.

Results
-------
BenchmarkDotNet v0.13.10, Windows 11 (10.0.22621.2861/22H2/2022Update/SunValley2)
12th Gen Intel Core i7-1270P, 1 CPU, 16 logical and 12 physical cores
.NET SDK 8.0.100
  [Host]     : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2
  Job-AODULB : .NET Framework 4.8.1 (4.8.9181.0), X64 RyuJIT VectorSize=256
  Job-AZPVRO : .NET Framework 4.8.1 (4.8.9181.0), X64 RyuJIT VectorSize=256
  Job-FTJSLO : .NET 6.0.25 (6.0.2523.51912), X64 RyuJIT AVX2
  Job-XMQQAK : .NET 6.0.25 (6.0.2523.51912), X64 RyuJIT AVX2
  Job-IPTUCK : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2
  Job-YAGJLP : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2
  Job-DJZUQL : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2
  Job-FFPERJ : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2

| Method     | Runtime            | BuildConfiguration | PGO | ReadSize | Mean     | Error   | StdDev  | Ratio | Rank |
|----------- |------------------- |------------------- |---- |--------- |---------:|--------:|--------:|------:|-----:|
| Decompress | .NET Framework 4.8 | Previous           | N   | 16384    | 340.4 us | 2.56 us | 2.39 us |  1.00 |    2 |
| Decompress | .NET Framework 4.8 | Default            | N   | 16384    | 332.2 us | 2.64 us | 2.34 us |  0.98 |    1 |
|            |                    |                    |     |          |          |         |         |       |      |
| Decompress | .NET 6.0           | Previous           | N   | 16384    | 167.5 us | 1.11 us | 1.04 us |  1.00 |    1 |
| Decompress | .NET 6.0           | Default            | N   | 16384    | 167.5 us | 2.44 us | 2.16 us |  1.00 |    1 |
|            |                    |                    |     |          |          |         |         |       |      |
| Decompress | .NET 8.0           | Previous           | N   | 16384    | 200.7 us | 1.19 us | 1.06 us |  1.00 |    2 |
| Decompress | .NET 8.0           | Default            | N   | 16384    | 191.7 us | 1.47 us | 1.30 us |  0.96 |    1 |
|            |                    |                    |     |          |          |         |         |       |      |
| Decompress | .NET 8.0           | Previous           | Y   | 16384    | 199.1 us | 0.94 us | 0.88 us |  1.00 |    2 |
| Decompress | .NET 8.0           | Default            | Y   | 16384    | 189.9 us | 0.91 us | 0.85 us |  0.95 |    1 |

Note: Benchmarks include other improvements since 1.1.3
  • Loading branch information
brantburnett committed Jan 6, 2024
1 parent 777936b commit 003a82a
Show file tree
Hide file tree
Showing 2 changed files with 10 additions and 6 deletions.
6 changes: 5 additions & 1 deletion Snappier/Internal/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ public enum ChunkType : byte
UncompressedData = 0x01,
SkippableChunk = 0x80, // If this bit is set, we can safely skip the chunk if unknown
Padding = 0xfe,
StreamIdentifier = 0xff
StreamIdentifier = 0xff,

// This is not part of the spec, but having this extra value representing null avoids
// the cost of wrapping in a Nullable<T>
Null = 0xfd,
}

public const byte Literal = 0b00;
Expand Down
10 changes: 5 additions & 5 deletions Snappier/Internal/SnappyStreamDecompressor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal sealed class SnappyStreamDecompressor : IDisposable

private readonly byte[] _scratch = new byte[ScratchBufferSize];
private int _scratchLength;
private Constants.ChunkType? _chunkType;
private Constants.ChunkType _chunkType = Constants.ChunkType.Null;
private int _chunkSize;
private int _chunkBytesProcessed;
private uint _expectedChunkCrc;
Expand All @@ -40,7 +40,7 @@ public int Decompress(Span<byte> buffer)
// ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
switch (_chunkType)
{
case null:
case Constants.ChunkType.Null:
// Not in a chunk, read the chunk type and size

uint rawChunkHeader = ReadChunkHeader(ref input);
Expand Down Expand Up @@ -106,7 +106,7 @@ public int Decompress(Span<byte> buffer)
if (_decompressor.EndOfFile)
{
// Completed reading the chunk
_chunkType = null;
_chunkType = Constants.ChunkType.Null;

uint crc = Crc32CAlgorithm.ApplyMask(_chunkCrc);
if (_expectedChunkCrc != crc)
Expand Down Expand Up @@ -149,7 +149,7 @@ public int Decompress(Span<byte> buffer)
if (_chunkBytesProcessed >= _chunkSize)
{
// Completed reading the chunk
_chunkType = null;
_chunkType = Constants.ChunkType.Null;

uint crc = Crc32CAlgorithm.ApplyMask(_chunkCrc);
if (_expectedChunkCrc != crc)
Expand All @@ -176,7 +176,7 @@ public int Decompress(Span<byte> buffer)
if (_chunkBytesProcessed >= _chunkSize)
{
// Completed reading the chunk
_chunkType = null;
_chunkType = Constants.ChunkType.Null;
}

break;
Expand Down

0 comments on commit 003a82a

Please sign in to comment.