Skip to content

Commit 6b91beb

Browse files
authored
Merge pull request #35 from ZEISS-PiWeb/feature/Jan/DecodingPerformance
Feature/jan/decoding performance
2 parents 7c00225 + 287a0bc commit 6b91beb

9 files changed

+354
-115
lines changed

src/PiWeb.Volume/Block/BlockInfo.cs

+5-6
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,13 @@ internal readonly record struct BlockInfo( ushort ValueCount, bool IsFirstValueS
5555
#region methods
5656

5757
/// <summary>
58-
/// Reads the <see cref="BlockInfo"/> from the specified <paramref name="reader"/>.
58+
/// Reads the <see cref="BlockInfo"/> from the specified <paramref name="value"/>.
5959
/// </summary>
60-
public static BlockInfo Read( BinaryReader reader )
60+
public static BlockInfo Create( ushort value )
6161
{
62-
var resultLength = reader.ReadUInt16();
63-
var valueCount = resultLength & ValueCountMask;
64-
var isFirstValueShort = ( resultLength & IsFirstValueShortMask ) >> IsFirstValueShortOffset;
65-
var areOtherValuesShort = ( resultLength & AreOtherValuesShortMask ) >> AreOtherValuesShortOffset;
62+
var valueCount = value & ValueCountMask;
63+
var isFirstValueShort = ( value & IsFirstValueShortMask ) >> IsFirstValueShortOffset;
64+
var areOtherValuesShort = ( value & AreOtherValuesShortMask ) >> AreOtherValuesShortOffset;
6665

6766
return new BlockInfo( (ushort)valueCount, isFirstValueShort > 0, areOtherValuesShort > 0 );
6867
}

src/PiWeb.Volume/Block/BlockVolume.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,8 @@ public override void GetSlice(
190190
CancellationToken ct = default )
191191
{
192192
var sw = Stopwatch.StartNew();
193-
var sliceRangeCollector = new BlockVolumeSliceRangeCollector( this, slice, buffer );
194-
sliceRangeCollector.CollectSliceRanges( progress, ct );
193+
var sliceCollector = new BlockVolumeSliceCollector( this, slice, buffer );
194+
sliceCollector.CollectSlice( progress, ct );
195195

196196
logger?.Log( LogLevel.Info, $"Extracted '{slice}' in {sw.ElapsedMilliseconds} ms." );
197197
}

src/PiWeb.Volume/Block/BlockVolumeDecoder.cs

+58-27
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ namespace Zeiss.PiWeb.Volume.Block;
1515

1616
using System;
1717
using System.Buffers;
18-
using System.IO;
1918
using System.Runtime.InteropServices;
19+
using System.Runtime.Intrinsics;
2020
using System.Threading;
2121
using System.Threading.Tasks;
2222

@@ -41,37 +41,49 @@ internal static void Decode( byte[] data,
4141
IProgress<VolumeSliceDefinition>? progress = null,
4242
CancellationToken ct = default )
4343
{
44-
var reader = new BinaryReader( new MemoryStream( data ) );
45-
var (_, sizeX, sizeY, sizeZ, quantization) = BlockVolumeMetaData.Read( reader );
44+
var header = BlockVolumeMetaData.Create( data );
45+
var (_, sizeX, sizeY, sizeZ, quantization) = header;
4646
var (bcx, bcy, bcz) = BlockVolume.GetBlockCount( sizeX, sizeY, sizeZ );
4747
var blockCount = bcx * bcy;
4848
var encodedBlockInfos = new EncodedBlockInfo[ blockCount ];
49+
var position = BlockVolumeMetaData.HeaderLength;
50+
var dataSpan = data.AsSpan();
51+
52+
Quantization.Invert( quantization );
4953

5054
for( ushort biz = 0; biz < bcz; biz++ )
5155
{
5256
ct.ThrowIfCancellationRequested();
5357

54-
var layerLength = reader.ReadInt32();
58+
var layerLength = MemoryMarshal.Read<int>( dataSpan.Slice( position, sizeof( int ) ) );
59+
position += sizeof( int );
5560
if( layerPredicate?.Invoke( biz ) is false )
5661
{
57-
reader.BaseStream.Seek( layerLength, SeekOrigin.Current );
62+
position += layerLength;
5863
continue;
5964
}
6065

61-
ReadBlockInfos( reader, blockCount, encodedBlockInfos );
66+
ReadLayer( dataSpan, position, blockCount, encodedBlockInfos );
6267
DecodeLayer( data, encodedBlockInfos, bcx, bcy, biz, quantization, blockPredicate, blockAction );
6368

6469
progress?.Report( new VolumeSliceDefinition( Direction.Z, (ushort)( biz * BlockVolume.N ) ) );
70+
71+
position += layerLength;
6572
}
6673
}
6774

68-
private static void ReadBlockInfos( BinaryReader reader, int blockCount, EncodedBlockInfo[] encodedBlockInfos )
75+
private static void ReadLayer( ReadOnlySpan<byte> dataSpan, int position, int blockCount, EncodedBlockInfo[] encodedBlockInfos )
6976
{
7077
for( var i = 0; i < blockCount; i++ )
7178
{
72-
var encodedBlockInfo = EncodedBlockInfo.Read( reader );
73-
reader.BaseStream.Seek( encodedBlockInfo.Info.Length, SeekOrigin.Current );
79+
var value = MemoryMarshal.Read<ushort>( dataSpan[ position.. ] );
80+
81+
position += sizeof( ushort );
82+
83+
var encodedBlockInfo = new EncodedBlockInfo( position, BlockInfo.Create( value ) );
7484
encodedBlockInfos[ i ] = encodedBlockInfo;
85+
86+
position += encodedBlockInfo.Info.Length;
7587
}
7688
}
7789

@@ -85,18 +97,29 @@ private static void DecodeLayer(
8597
BlockPredicate? blockPredicate,
8698
BlockAction blockAction )
8799
{
100+
#if DEBUG
101+
var buffers = ( ArrayPool<double>.Shared.Rent( BlockVolume.N3 ),
102+
ArrayPool<double>.Shared.Rent( BlockVolume.N3 ),
103+
ArrayPool<byte>.Shared.Rent( BlockVolume.N3 ) );
104+
for( var index = 0; index < blockCountX * blockCountY; index++ )
105+
#else
88106
Parallel.For( 0, blockCountX * blockCountY, () => (
89107
ArrayPool<double>.Shared.Rent( BlockVolume.N3 ),
90108
ArrayPool<double>.Shared.Rent( BlockVolume.N3 ),
91109
ArrayPool<byte>.Shared.Rent( BlockVolume.N3 )
92110
), ( index, _, buffers ) =>
111+
#endif
93112
{
94113
var blockIndexX = index % blockCountX;
95114
var blockIndexY = index / blockCountX;
96115
var blockIndex = new BlockIndex( (ushort)blockIndexX, (ushort)blockIndexY, blockIndexZ );
97116

98117
if( blockPredicate?.Invoke( blockIndex ) == false )
118+
#if DEBUG
119+
continue;
120+
#else
99121
return buffers;
122+
#endif
100123

101124
var doubleBuffer1 = buffers.Item1.AsSpan( 0, BlockVolume.N3 );
102125
var doubleBuffer2 = buffers.Item2.AsSpan( 0, BlockVolume.N3 );
@@ -109,18 +132,27 @@ private static void DecodeLayer(
109132
//2. ZigZag
110133
ZigZag.Reverse( doubleBuffer1, doubleBuffer2 );
111134

135+
var nonEmptyVectors = FindNonEmptyVectors( doubleBuffer2 );
136+
112137
//3. Quantization
113138
Quantization.Apply( quantization.AsSpan(), doubleBuffer2 );
114139

115140
//4. Cosine transform
116-
DiscreteCosineTransform.Transform( doubleBuffer2, doubleBuffer1, true );
141+
DiscreteCosineTransform.Transform( doubleBuffer2, doubleBuffer1, true, nonEmptyVectors );
117142

118143
//5. Discretization
119144
for( var i = 0; i < BlockVolume.N3; i++ )
120145
byteBuffer[ i ] = (byte)Math.Clamp( doubleBuffer1[ i ], byte.MinValue, byte.MaxValue );
121146

122147
blockAction( byteBuffer, blockIndex );
123148

149+
#if DEBUG
150+
}
151+
152+
ArrayPool<double>.Shared.Return( buffers.Item1 );
153+
ArrayPool<double>.Shared.Return( buffers.Item2 );
154+
ArrayPool<byte>.Shared.Return( buffers.Item3 );
155+
#else
124156
return buffers;
125157
},
126158
buffers =>
@@ -129,6 +161,21 @@ private static void DecodeLayer(
129161
ArrayPool<double>.Shared.Return( buffers.Item2 );
130162
ArrayPool<byte>.Shared.Return( buffers.Item3 );
131163
} );
164+
#endif
165+
}
166+
167+
private static ulong FindNonEmptyVectors( Span<double> values )
168+
{
169+
var vectors = MemoryMarshal.Cast<double, Vector512<double>>( values );
170+
var result = 0UL;
171+
172+
for( ushort i = 0; i < BlockVolume.N2; i++ )
173+
{
174+
if( Vector512.Sum( Vector512.Abs( vectors[ i ] ) ) > 1e-12 )
175+
result |= 1UL << i;
176+
}
177+
178+
return result;
132179
}
133180

134181
private static void ReadBlock( ReadOnlySpan<byte> data, EncodedBlockInfo blockInfo, Span<double> result )
@@ -167,23 +214,7 @@ private static void ReadBlock( ReadOnlySpan<byte> data, EncodedBlockInfo blockIn
167214

168215
#endregion
169216

170-
private readonly record struct EncodedBlockInfo( int StartIndex, BlockInfo Info )
171-
{
172-
#region methods
173-
174-
/// <summary>
175-
/// Reads the encoded block info from the specified <paramref name="reader"/>
176-
/// </summary>
177-
public static EncodedBlockInfo Read( BinaryReader reader )
178-
{
179-
var info = BlockInfo.Read( reader );
180-
var startIndex = (int)reader.BaseStream.Position;
181-
182-
return new EncodedBlockInfo( startIndex, info );
183-
}
184-
185-
#endregion
186-
}
217+
private readonly record struct EncodedBlockInfo( int StartIndex, BlockInfo Info );
187218

188219
internal delegate void BlockAction( ReadOnlySpan<byte> data, BlockIndex index );
189220

src/PiWeb.Volume/Block/BlockVolumeDecompressor.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,13 @@ private void CopyBlockToResult( ReadOnlySpan<byte> block, BlockIndex index )
7979
for( var bz = 0; bz < sz; bz++ )
8080
{
8181
var gz = index.Z * BlockVolume.N + bz;
82+
var oz = bz * BlockVolume.N2;
8283
var sliceData = _Result[ gz ].AsSpan();
8384

8485
for( var by = 0; by < sy; by++ )
8586
{
8687
var gy = index.Y * BlockVolume.N + by;
87-
block.Slice( bz * BlockVolume.N2 + by * BlockVolume.N, sx ).CopyTo( sliceData.Slice( gy * _SizeX + gx, sx ) );
88+
block.Slice( oz + by * BlockVolume.N, sx ).CopyTo( sliceData.Slice( gy * _SizeX + gx, sx ) );
8889
}
8990
}
9091
}

src/PiWeb.Volume/Block/BlockVolumeMetaData.cs

+22-10
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ namespace Zeiss.PiWeb.Volume.Block;
1414

1515
using System;
1616
using System.IO;
17+
using System.Runtime.InteropServices;
1718

1819
#endregion
1920

2021
/// <summary>
2122
/// Holds information that are needed to encode and decode a block volume.
2223
/// </summary>
23-
public readonly record struct BlockVolumeMetaData( uint Version, ushort SizeX, ushort SizeY, ushort SizeZ, double[] Quantization )
24+
public record BlockVolumeMetaData( uint Version, ushort SizeX, ushort SizeY, ushort SizeZ, double[] Quantization )
2425
{
2526
#region methods
2627

@@ -40,26 +41,37 @@ public void Write( BinaryWriter writer )
4041
}
4142

4243
/// <summary>
43-
/// Reads the metadata from the specified <paramref name="reader"/>
44+
/// Reads the metadata from the specified <paramref name="data"/>
4445
/// </summary>
45-
public static BlockVolumeMetaData Read( BinaryReader reader )
46+
public static BlockVolumeMetaData Create( ReadOnlySpan<byte> data )
4647
{
47-
var header = reader.ReadUInt32();
48+
var position = 0;
49+
var header = MemoryMarshal.Read<uint>( data[ position.. ] );
50+
position += sizeof( uint );
4851
if( header != BlockVolume.FileHeader )
4952
throw new FormatException( $"Encountered unexpected file header 0x{header:x8}, expected 0x{BlockVolume.FileHeader:x8}" );
5053

51-
var version = reader.ReadUInt32();
54+
var version = MemoryMarshal.Read<uint>( data[ position.. ] );
55+
position += sizeof( uint );
5256
if( version != BlockVolume.Version )
5357
throw new FormatException( $"Encountered unexpected file header '{version}', expected {BlockVolume.Version}" );
5458

55-
var sizeX = reader.ReadUInt16();
56-
var sizeY = reader.ReadUInt16();
57-
var sizeZ = reader.ReadUInt16();
59+
var sizeX = MemoryMarshal.Read<ushort>( data[ position.. ] );
60+
position += sizeof( ushort );
61+
var sizeY = MemoryMarshal.Read<ushort>( data[ position.. ] );
62+
position += sizeof( ushort );
63+
var sizeZ = MemoryMarshal.Read<ushort>( data[ position.. ] );
64+
position += sizeof( ushort );
5865

59-
var quantization = Block.Quantization.Read( reader, true );
66+
var quantization = MemoryMarshal.Cast<byte, double>( data.Slice( position, BlockVolume.N3 * sizeof( double ) ) );
6067

61-
return new BlockVolumeMetaData( version, sizeX, sizeY, sizeZ, quantization );
68+
return new BlockVolumeMetaData( version, sizeX, sizeY, sizeZ, quantization.ToArray() );
6269
}
6370

6471
#endregion
72+
73+
/// <summary>
74+
/// The number of bytes the header consists of.
75+
/// </summary>
76+
public static int HeaderLength => 2 * sizeof( uint ) + 3 * sizeof( ushort ) + BlockVolume.N3 * sizeof( double );
6577
}

0 commit comments

Comments
 (0)