Skip to content

Commit 82396fa

Browse files
bigjt-devbjornharrtellaardappel
authored
[C#] Improve Span<> utilization (#8588)
There are a couple instances where the ByteBuffer's Span property was accessed in a loop. + Extracted the use of the property outside of the loop to save a few cpu cycles. Access to the allocator's internal buffer isn't exposed as a ReadOnlySpan<byte> from the ByteBuffer or the FlatBufferBuilder. + Added a few convenience functions to access the buffer using a ReadOnlySpan<byte>. There are a few cases where built in Span extensions can be used to run optimized code. + Added the use of Span.Fill() and ReadOnlySpan.SequenceCompareTo to replace existing loops. Co-authored-by: Björn Harrtell <[email protected]> Co-authored-by: Wouter van Oortmerssen <[email protected]>
1 parent a6b337f commit 82396fa

File tree

3 files changed

+45
-6
lines changed

3 files changed

+45
-6
lines changed

net/FlatBuffers/ByteBuffer.cs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,11 @@ public byte[] ToFullArray()
275275
}
276276

277277
#if ENABLE_SPAN_T && UNSAFE_BYTEBUFFER
278+
public ReadOnlySpan<byte> ToSizedReadOnlySpan()
279+
{
280+
return _buffer.ReadOnlySpan.Slice(Position, Length - Position);
281+
}
282+
278283
public ReadOnlyMemory<byte> ToReadOnlyMemory(int pos, int len)
279284
{
280285
return _buffer.ReadOnlyMemory.Slice(pos, len);
@@ -289,6 +294,11 @@ public Span<byte> ToSpan(int pos, int len)
289294
{
290295
return _buffer.Span.Slice(pos, len);
291296
}
297+
298+
public ReadOnlySpan<byte> ToReadOnlySpan(int pos, int len)
299+
{
300+
return _buffer.ReadOnlySpan.Slice(pos, len);
301+
}
292302
#else
293303
public ArraySegment<byte> ToArraySegment(int pos, int len)
294304
{
@@ -380,38 +390,40 @@ protected ulong ReadLittleEndian(int offset, int count)
380390
#elif ENABLE_SPAN_T
381391
protected void WriteLittleEndian(int offset, int count, ulong data)
382392
{
393+
Span<byte> span = _buffer.Span.Slice(offset, count);
383394
if (BitConverter.IsLittleEndian)
384395
{
385396
for (int i = 0; i < count; i++)
386397
{
387-
_buffer.Span[offset + i] = (byte)(data >> i * 8);
398+
span[i] = (byte)(data >> i * 8);
388399
}
389400
}
390401
else
391402
{
392403
for (int i = 0; i < count; i++)
393404
{
394-
_buffer.Span[offset + count - 1 - i] = (byte)(data >> i * 8);
405+
span[count - 1 - i] = (byte)(data >> i * 8);
395406
}
396407
}
397408
}
398409

399410
protected ulong ReadLittleEndian(int offset, int count)
400411
{
401412
AssertOffsetAndLength(offset, count);
413+
ReadOnlySpan<byte> span = _buffer.ReadOnlySpan.Slice(offset, count);
402414
ulong r = 0;
403415
if (BitConverter.IsLittleEndian)
404416
{
405417
for (int i = 0; i < count; i++)
406418
{
407-
r |= (ulong)_buffer.Span[offset + i] << i * 8;
419+
r |= (ulong)span[i] << i * 8;
408420
}
409421
}
410422
else
411423
{
412424
for (int i = 0; i < count; i++)
413425
{
414-
r |= (ulong)_buffer.Span[offset + count - 1 - i] << i * 8;
426+
r |= (ulong)span[count - 1 - i] << i * 8;
415427
}
416428
}
417429
return r;
@@ -445,8 +457,7 @@ public void PutByte(int offset, byte value, int count)
445457
{
446458
AssertOffsetAndLength(offset, sizeof(byte) * count);
447459
Span<byte> span = _buffer.Span.Slice(offset, count);
448-
for (var i = 0; i < span.Length; ++i)
449-
span[i] = value;
460+
span.Fill(value);
450461
}
451462
#else
452463
public void PutSbyte(int offset, sbyte value)

net/FlatBuffers/FlatBufferBuilder.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,21 @@ public byte[] SizedByteArray()
957957
return _bb.ToSizedArray();
958958
}
959959

960+
#if ENABLE_SPAN_T && UNSAFE_BYTEBUFFER
961+
/// <summary>
962+
/// A utility function return the ByteBuffer data as a
963+
/// `ReadOnlySpan<byte>`.
964+
/// </summary>
965+
/// <returns>
966+
/// A `ReadOnlySpan<byte>` that references the internal
967+
/// ByteBuffer data.
968+
/// </returns>
969+
public ReadOnlySpan<byte> SizedReadOnlySpan()
970+
{
971+
return _bb.ToSizedReadOnlySpan();
972+
}
973+
#endif
974+
960975
/// <summary>
961976
/// Finalize a buffer, pointing to the given `rootTable`.
962977
/// </summary>

net/FlatBuffers/Table.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,12 @@ public static int CompareStrings(int offset_1, int offset_2, ByteBuffer bb)
183183
var len_2 = bb.GetInt(offset_2);
184184
var startPos_1 = offset_1 + sizeof(int);
185185
var startPos_2 = offset_2 + sizeof(int);
186+
187+
#if ENABLE_SPAN_T && UNSAFE_BYTEBUFFER
188+
var span_1 = bb.ToReadOnlySpan(startPos_1, len_1);
189+
var span_2 = bb.ToReadOnlySpan(startPos_2, len_2);
190+
return span_1.SequenceCompareTo(span_2);
191+
#else
186192
var len = Math.Min(len_1, len_2);
187193
for(int i = 0; i < len; i++) {
188194
byte b1 = bb.Get(i + startPos_1);
@@ -191,6 +197,7 @@ public static int CompareStrings(int offset_1, int offset_2, ByteBuffer bb)
191197
return b1 - b2;
192198
}
193199
return len_1 - len_2;
200+
#endif
194201
}
195202

196203
// Compare string from the ByteBuffer with the string object
@@ -200,13 +207,19 @@ public static int CompareStrings(int offset_1, byte[] key, ByteBuffer bb)
200207
var len_1 = bb.GetInt(offset_1);
201208
var len_2 = key.Length;
202209
var startPos_1 = offset_1 + sizeof(int);
210+
#if ENABLE_SPAN_T && UNSAFE_BYTEBUFFER
211+
ReadOnlySpan<byte> span = bb.ToReadOnlySpan(startPos_1, len_1);
212+
ReadOnlySpan<byte> keySpan = key;
213+
return span.SequenceCompareTo(keySpan);
214+
#else
203215
var len = Math.Min(len_1, len_2);
204216
for (int i = 0; i < len; i++) {
205217
byte b = bb.Get(i + startPos_1);
206218
if (b != key[i])
207219
return b - key[i];
208220
}
209221
return len_1 - len_2;
222+
#endif
210223
}
211224
}
212225
}

0 commit comments

Comments
 (0)