Skip to content

Commit

Permalink
Merge pull request #1100 from bollhals/improvBasicProperties
Browse files Browse the repository at this point in the history
improve basic properties read / write
  • Loading branch information
michaelklishin authored Oct 20, 2021
2 parents cdd459c + 5304636 commit 9922bc1
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 102 deletions.
40 changes: 32 additions & 8 deletions projects/Benchmarks/WireFormatting/PrimitivesSerialization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace RabbitMQ.Benchmarks
[BenchmarkCategory("Primitives")]
public class PrimitivesBase
{
protected Memory<byte> _buffer = new byte[16];
protected readonly Memory<byte> _buffer = new byte[16];

[GlobalSetup]
public virtual void Setup() { }
Expand All @@ -22,10 +22,18 @@ public class PrimitivesBool : PrimitivesBase
public override void Setup() => WireFormatting.WriteBits(ref _buffer.Span.GetStart(), true, false, true, false, true);

[Benchmark]
public int BoolRead2() => WireFormatting.ReadBits(_buffer.Span, out bool _, out bool _);
public (bool, bool) BoolRead2()
{
WireFormatting.ReadBits(_buffer.Span, out bool v1, out bool v2);
return (v1, v2);
}

[Benchmark]
public int BoolRead5() => WireFormatting.ReadBits(_buffer.Span, out bool _, out bool _, out bool _, out bool _, out bool _);
public (bool, bool, bool, bool, bool) BoolRead5()
{
WireFormatting.ReadBits(_buffer.Span, out bool v1, out bool v2, out bool v3, out bool v4, out bool v5);
return (v1, v2, v3, v4, v5);
}

[Benchmark]
[Arguments(true, false)]
Expand All @@ -52,7 +60,11 @@ public class PrimitivesLong : PrimitivesBase
public override void Setup() => WireFormatting.WriteLong(ref _buffer.Span.GetStart(), 12345u);

[Benchmark]
public int LongRead() => WireFormatting.ReadLong(_buffer.Span, out _);
public uint LongRead()
{
WireFormatting.ReadLong(_buffer.Span, out uint v1);
return v1;
}

[Benchmark]
[Arguments(12345u)]
Expand All @@ -64,7 +76,11 @@ public class PrimitivesLonglong : PrimitivesBase
public override void Setup() => WireFormatting.WriteLonglong(ref _buffer.Span.GetStart(), 12345ul);

[Benchmark]
public int LonglongRead() => WireFormatting.ReadLonglong(_buffer.Span, out _);
public ulong LonglongRead()
{
WireFormatting.ReadLonglong(_buffer.Span, out ulong v1);
return v1;
}

[Benchmark]
[Arguments(12345ul)]
Expand All @@ -76,7 +92,11 @@ public class PrimitivesShort : PrimitivesBase
public override void Setup() => WireFormatting.WriteShort(ref _buffer.Span.GetStart(), 12345);

[Benchmark]
public int ShortRead() => WireFormatting.ReadShort(_buffer.Span, out _);
public ushort ShortRead()
{
WireFormatting.ReadShort(_buffer.Span, out ushort v1);
return v1;
}

[Benchmark]
[Arguments(12345)]
Expand All @@ -85,12 +105,16 @@ public class PrimitivesShort : PrimitivesBase

public class PrimitivesTimestamp : PrimitivesBase
{
AmqpTimestamp _timestamp = new AmqpTimestamp(DateTimeOffset.UtcNow.ToUnixTimeSeconds());
private AmqpTimestamp _timestamp = new AmqpTimestamp(DateTimeOffset.UtcNow.ToUnixTimeSeconds());

public override void Setup() => WireFormatting.WriteTimestamp(ref _buffer.Span.GetStart(), _timestamp);

[Benchmark]
public int TimestampRead() => WireFormatting.ReadTimestamp(_buffer.Span, out _);
public AmqpTimestamp TimestampRead()
{
WireFormatting.ReadTimestamp(_buffer.Span, out AmqpTimestamp v1);
return v1;
}

[Benchmark]
public int TimestampWrite() => WireFormatting.WriteTimestamp(ref _buffer.Span.GetStart(), _timestamp);
Expand Down
176 changes: 129 additions & 47 deletions projects/RabbitMQ.Client/client/framing/BasicProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,60 +200,142 @@ public BasicProperties()

public BasicProperties(ReadOnlySpan<byte> span)
{
int offset = WireFormatting.ReadBits(span,
out bool contentType_present,
out bool contentEncoding_present,
out bool headers_present,
out bool deliveryMode_present,
out bool priority_present,
out bool correlationId_present,
out bool replyTo_present,
out bool expiration_present,
out bool messageId_present,
out bool timestamp_present,
out bool type_present,
out bool userId_present,
out bool appId_present,
out bool clusterId_present);
if (contentType_present) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _contentType); }
if (contentEncoding_present) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _contentEncoding); }
if (headers_present) { offset += WireFormatting.ReadDictionary(span.Slice(offset), out var tmpDirectory); _headers = tmpDirectory; }
if (deliveryMode_present) { _deliveryMode = span[offset++]; }
if (priority_present) { _priority = span[offset++]; }
if (correlationId_present) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _correlationId); }
if (replyTo_present) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _replyTo); }
if (expiration_present) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _expiration); }
if (messageId_present) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _messageId); }
if (timestamp_present) { offset += WireFormatting.ReadTimestamp(span.Slice(offset), out _timestamp); }
if (type_present) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _type); }
if (userId_present) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _userId); }
if (appId_present) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _appId); }
if (clusterId_present) { WireFormatting.ReadShortstr(span.Slice(offset), out _clusterId); }
int offset = 2;
ref readonly byte bits = ref span[0];
if (bits.IsBitSet(ContentTypeBit)) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _contentType); }
if (bits.IsBitSet(ContentEncodingBit)) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _contentEncoding); }
if (bits.IsBitSet(HeaderBit)) { offset += WireFormatting.ReadDictionary(span.Slice(offset), out var tmpDirectory); _headers = tmpDirectory; }
if (bits.IsBitSet(DeliveryModeBit)) { _deliveryMode = span[offset++]; }
if (bits.IsBitSet(PriorityBit)) { _priority = span[offset++]; }
if (bits.IsBitSet(CorrelationIdBit)) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _correlationId); }
if (bits.IsBitSet(ReplyToBit)) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _replyTo); }
if (bits.IsBitSet(ExpirationBit)) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _expiration); }

bits = ref span[1];
if (bits.IsBitSet(MessageIdBit)) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _messageId); }
if (bits.IsBitSet(TimestampBit)) { offset += WireFormatting.ReadTimestamp(span.Slice(offset), out _timestamp); }
if (bits.IsBitSet(TypeBit)) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _type); }
if (bits.IsBitSet(UserIdBit)) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _userId); }
if (bits.IsBitSet(AppIdBit)) { offset += WireFormatting.ReadShortstr(span.Slice(offset), out _appId); }
if (bits.IsBitSet(ClusterIdBit)) { WireFormatting.ReadShortstr(span.Slice(offset), out _clusterId); }
}

public override ushort ProtocolClassId => 60;
public override string ProtocolClassName => "basic";

//----------------------------------
// First byte
//----------------------------------
private const byte ContentTypeBit = 7;
private const byte ContentEncodingBit = 6;
private const byte HeaderBit = 5;
private const byte DeliveryModeBit = 4;
private const byte PriorityBit = 3;
private const byte CorrelationIdBit = 2;
private const byte ReplyToBit = 1;
private const byte ExpirationBit = 0;

//----------------------------------
// Second byte
//----------------------------------
private const byte MessageIdBit = 7;
private const byte TimestampBit = 6;
private const byte TypeBit = 5;
private const byte UserIdBit = 4;
private const byte AppIdBit = 3;
private const byte ClusterIdBit = 2;

internal override int WritePropertiesTo(Span<byte> span)
{
int offset = WireFormatting.WriteBits(ref span.GetStart(),
IsContentTypePresent(), IsContentEncodingPresent(), IsHeadersPresent(), IsDeliveryModePresent(), IsPriorityPresent(),
IsCorrelationIdPresent(), IsReplyToPresent(), IsExpirationPresent(), IsMessageIdPresent(), IsTimestampPresent(),
IsTypePresent(), IsUserIdPresent(), IsAppIdPresent(), IsClusterIdPresent());
if (IsContentTypePresent()) { offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _contentType); }
if (IsContentEncodingPresent()) { offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _contentEncoding); }
if (IsHeadersPresent()) { offset += WireFormatting.WriteTable(ref span.GetOffset(offset), _headers); }
if (IsDeliveryModePresent()) { span[offset++] = _deliveryMode; }
if (IsPriorityPresent()) { span[offset++] = _priority; }
if (IsCorrelationIdPresent()) { offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _correlationId); }
if (IsReplyToPresent()) { offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _replyTo); }
if (IsExpirationPresent()) { offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _expiration); }
if (IsMessageIdPresent()) { offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _messageId); }
if (IsTimestampPresent()) { offset += WireFormatting.WriteTimestamp(ref span.GetOffset(offset), _timestamp); }
if (IsTypePresent()) { offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _type); }
if (IsUserIdPresent()) { offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _userId); }
if (IsAppIdPresent()) { offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _appId); }
if (IsClusterIdPresent()) { offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _clusterId); }
int offset = 2;
ref byte bitValue = ref span.GetStart();
bitValue = 0;
if (IsContentTypePresent())
{
bitValue.SetBit(ContentTypeBit);
offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _contentType);
}

if (IsContentEncodingPresent())
{
bitValue.SetBit(ContentEncodingBit);
offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _contentEncoding);
}

if (IsHeadersPresent())
{
bitValue.SetBit(HeaderBit);
offset += WireFormatting.WriteTable(ref span.GetOffset(offset), _headers);
}

if (IsDeliveryModePresent())
{
bitValue.SetBit(DeliveryModeBit);
span.GetOffset(offset++) = _deliveryMode;
}

if (IsPriorityPresent())
{
bitValue.SetBit(PriorityBit);
span.GetOffset(offset++) = _priority;
}

if (IsCorrelationIdPresent())
{
bitValue.SetBit(CorrelationIdBit);
offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _correlationId);
}

if (IsReplyToPresent())
{
bitValue.SetBit(ReplyToBit);
offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _replyTo);
}

if (IsExpirationPresent())
{
bitValue.SetBit(ExpirationBit);
offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _expiration);
}

bitValue = ref span.GetOffset(1);
bitValue = 0;
if (IsMessageIdPresent())
{
bitValue.SetBit(MessageIdBit);
offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _messageId);
}

if (IsTimestampPresent())
{
bitValue.SetBit(TimestampBit);
offset += WireFormatting.WriteTimestamp(ref span.GetOffset(offset), _timestamp);
}

if (IsTypePresent())
{
bitValue.SetBit(TypeBit);
offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _type);
}

if (IsUserIdPresent())
{
bitValue.SetBit(UserIdBit);
offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _userId);
}

if (IsAppIdPresent())
{
bitValue.SetBit(AppIdBit);
offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _appId);
}

if (IsClusterIdPresent())
{
bitValue.SetBit(ClusterIdBit);
offset += WireFormatting.WriteShortstr(ref span.GetOffset(offset), _clusterId);
}

return offset;
}

Expand Down
29 changes: 2 additions & 27 deletions projects/RabbitMQ.Client/client/impl/WireFormatting.Read.cs
Original file line number Diff line number Diff line change
Expand Up @@ -238,31 +238,6 @@ public static int ReadBits(ReadOnlySpan<byte> span, out bool val1, out bool val2
return 1;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ReadBits(ReadOnlySpan<byte> span,
out bool val1, out bool val2, out bool val3, out bool val4, out bool val5,
out bool val6, out bool val7, out bool val8, out bool val9, out bool val10,
out bool val11, out bool val12, out bool val13, out bool val14)
{
byte bits = span[0];
val1 = (bits & 0b1000_0000) != 0;
val2 = (bits & 0b0100_0000) != 0;
val3 = (bits & 0b0010_0000) != 0;
val4 = (bits & 0b0001_0000) != 0;
val5 = (bits & 0b0000_1000) != 0;
val6 = (bits & 0b0000_0100) != 0;
val7 = (bits & 0b0000_0010) != 0;
val8 = (bits & 0b0000_0001) != 0;
bits = span[1];
val9 = (bits & 0b1000_0000) != 0;
val10 = (bits & 0b0100_0000) != 0;
val11 = (bits & 0b0010_0000) != 0;
val12 = (bits & 0b0001_0000) != 0;
val13 = (bits & 0b0000_1000) != 0;
val14 = (bits & 0b0000_0100) != 0;
return 2;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ReadShort(ReadOnlySpan<byte> span, out ushort value)
{
Expand Down Expand Up @@ -328,7 +303,7 @@ public static int ThrowSyntaxErrorException(uint byteCount)
private static int ThrowInvalidTableValue(char type)
=> throw new SyntaxErrorException($"Unrecognised type in table: {type}");

private static decimal ThrowInvalidDecimalScale(int scale)
=> throw new SyntaxErrorException($"Unrepresentable AMQP decimal table field: scale={scale}");
private static void ThrowInvalidDecimalScale(int scale)
=> throw new SyntaxErrorException($"Unrepresentable AMQP decimal table field: scale={scale}");
}
}
20 changes: 0 additions & 20 deletions projects/RabbitMQ.Client/client/impl/WireFormatting.Write.cs
Original file line number Diff line number Diff line change
Expand Up @@ -302,23 +302,6 @@ public static int WriteBits(ref byte destination, bool val1, bool val2, bool val
return 1;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int WriteBits(ref byte destination,
bool val1, bool val2, bool val3, bool val4, bool val5,
bool val6, bool val7, bool val8, bool val9, bool val10,
bool val11, bool val12, bool val13, bool val14)
{
int a = val8.ToByte() + val7.ToByte() * 2 + val6.ToByte() * 4 + val5.ToByte() * 8;
int b = val4.ToByte() + val3.ToByte() * 2 + val2.ToByte() * 4 + val1.ToByte() * 8;
destination = (byte)(a | (b << 4));

a = val14.ToByte() * 4 + val13.ToByte() * 8;
b = val12.ToByte() + val11.ToByte() * 2 + val10.ToByte() * 4 + val9.ToByte() * 8;

destination.GetOffset(1) = (byte)(a | (b << 4));
return 2;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int WriteLongstr(ref byte destination, ReadOnlySpan<byte> val)
{
Expand Down Expand Up @@ -500,9 +483,6 @@ public static int ThrowArgumentTooLong(int length)
public static int ThrowArgumentOutOfRangeException(int orig, int expected)
=> throw new ArgumentOutOfRangeException("span", $"Span has not enough space ({orig} instead of {expected})");

public static int ThrowArgumentOutOfRangeException(string val, int maxLength)
=> throw new ArgumentOutOfRangeException(nameof(val), val, $"Value exceeds the maximum allowed length of {maxLength} bytes.");

private static int ThrowWireFormattingException(decimal value)
=> throw new WireFormattingException("Decimal overflow in AMQP encoding", value);

Expand Down
12 changes: 12 additions & 0 deletions projects/RabbitMQ.Client/util/TypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ public static ref byte GetOffset(this ref byte source, int offset)
return ref Unsafe.Add(ref source, offset);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsBitSet(this in byte value, byte bitPosition)
{
return (value & (1 << bitPosition)) != 0;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SetBit(this ref byte value, byte bitPosition)
{
value |= (byte)(1 << bitPosition);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte ToByte(this ref bool source)
{
Expand Down

0 comments on commit 9922bc1

Please sign in to comment.