Skip to content

Commit 881b08b

Browse files
[Event Hub] PartitionResolver Jenkin3 optimizations (#50068)
* Optimized Jenkin3 lookup version 1 * Optimized Jenkin3 lookup version 2 * Update CHANGELOG.md * Update sdk/eventhub/Azure.Messaging.EventHubs/CHANGELOG.md * Update sdk/eventhub/Azure.Messaging.EventHubs/CHANGELOG.md --------- Co-authored-by: Daniel Marbach <[email protected]> Co-authored-by: Jesse Squire <[email protected]>
1 parent fef0a40 commit 881b08b

File tree

2 files changed

+50
-27
lines changed

2 files changed

+50
-27
lines changed

sdk/eventhub/Azure.Messaging.EventHubs/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
## 5.13.0-beta.1 (Unreleased)
44

5+
### Acknowledgments
6+
7+
Thank you to our developer community members who helped to make the Event Hubs client libraries better with their contributions to this release:
8+
9+
- Daniel Marbach _([GitHub](https://github.com/danielmarbach))_
10+
511
### Features Added
612

713
### Breaking Changes
@@ -10,6 +16,8 @@
1016

1117
### Other Changes
1218

19+
- Significantly improved the performance of the Jenkins3 hash computation used for partition key resolution. Across various input sizes, the updated implementation achieves up to 39% faster hash calculation, with the most notable gains seen for smaller keys (8–32 bytes), and consistent improvements across all sizes. The new approach maintains the exact bit-for-bit hash output while reducing overhead. _(A community contribution, courtesy of [danielmarbach](https://github.com/danielmarbach))_
20+
1321
## 5.12.1 (2025-04-09)
1422

1523
### Bugs Fixed

sdk/eventhub/Azure.Messaging.EventHubs/src/Core/PartitionResolver.cs

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Buffers;
66
using System.Buffers.Binary;
77
using System.Runtime.CompilerServices;
8+
using System.Runtime.InteropServices;
89
using System.Text;
910
using System.Threading;
1011

@@ -149,17 +150,31 @@ private static void ComputeHash(ReadOnlySpan<byte> data,
149150
out uint hash1,
150151
out uint hash2)
151152
{
152-
uint a, b, c;
153+
uint len = (uint)data.Length;
154+
uint a = 0xDEADBEEF + len + seed1;
155+
uint b = a;
156+
uint c = a + seed2;
153157

154-
a = b = c = (uint)(0xdeadbeef + data.Length + seed1);
155-
c += seed2;
158+
int chunks = data.Length > 12 ? (data.Length - 1) / 12 : 0;
156159

157-
int index = 0, size = data.Length;
158-
while (size > 12)
160+
ref byte ptr = ref MemoryMarshal.GetReference(data);
161+
for (int i = 0; i < chunks; i++)
159162
{
160-
a += BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(index));
161-
b += BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(index + 4));
162-
c += BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(index + 8));
163+
uint w0 = Unsafe.ReadUnaligned<uint>(ref ptr);
164+
uint w1 = Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref ptr, 4));
165+
uint w2 = Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref ptr, 8));
166+
ptr = ref Unsafe.Add(ref ptr, 12);
167+
168+
if (!BitConverter.IsLittleEndian)
169+
{
170+
w0 = BinaryPrimitives.ReverseEndianness(w0);
171+
w1 = BinaryPrimitives.ReverseEndianness(w1);
172+
w2 = BinaryPrimitives.ReverseEndianness(w2);
173+
}
174+
175+
a += w0;
176+
b += w1;
177+
c += w2;
163178

164179
a -= c;
165180
a ^= (c << 4) | (c >> 28);
@@ -184,51 +199,51 @@ private static void ComputeHash(ReadOnlySpan<byte> data,
184199
c -= b;
185200
c ^= (b << 4) | (b >> 28);
186201
b += a;
187-
188-
index += 12;
189-
size -= 12;
190202
}
191203

192-
switch (size)
204+
int consumed = chunks * 12;
205+
ref byte tail = ref Unsafe.Add(ref MemoryMarshal.GetReference(data), consumed);
206+
int left = data.Length - consumed;
207+
switch (left)
193208
{
194209
case 12:
195-
a += BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(index));
196-
b += BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(index + 4));
197-
c += BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(index + 8));
210+
a += BitConverter.IsLittleEndian ? Unsafe.ReadUnaligned<uint>(ref tail) : BinaryPrimitives.ReverseEndianness(Unsafe.ReadUnaligned<uint>(ref tail));
211+
b += BitConverter.IsLittleEndian ? Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref tail, 4)) : BinaryPrimitives.ReverseEndianness(Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref tail, 4)));
212+
c += BitConverter.IsLittleEndian ? Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref tail, 8)) : BinaryPrimitives.ReverseEndianness(Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref tail, 8)));
198213
break;
199214
case 11:
200-
c += ((uint)data[index + 10]) << 16;
215+
c += (uint)Unsafe.Add(ref tail, 10) << 16;
201216
goto case 10;
202217
case 10:
203-
c += ((uint)data[index + 9]) << 8;
218+
c += (uint)Unsafe.Add(ref tail, 9) << 8;
204219
goto case 9;
205220
case 9:
206-
c += (uint)data[index + 8];
221+
c += Unsafe.Add(ref tail, 8);
207222
goto case 8;
208223
case 8:
209-
b += BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(index + 4));
210-
a += BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(index));
224+
b += BitConverter.IsLittleEndian ? Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref tail, 4)) : BinaryPrimitives.ReverseEndianness(Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref tail, 4)));
225+
a += BitConverter.IsLittleEndian ? Unsafe.ReadUnaligned<uint>(ref tail) : BinaryPrimitives.ReverseEndianness(Unsafe.ReadUnaligned<uint>(ref tail));
211226
break;
212227
case 7:
213-
b += ((uint)data[index + 6]) << 16;
228+
b += (uint)Unsafe.Add(ref tail, 6) << 16;
214229
goto case 6;
215230
case 6:
216-
b += ((uint)data[index + 5]) << 8;
231+
b += (uint)Unsafe.Add(ref tail, 5) << 8;
217232
goto case 5;
218233
case 5:
219-
b += (uint)data[index + 4];
234+
b += Unsafe.Add(ref tail, 4);
220235
goto case 4;
221236
case 4:
222-
a += BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(index));
237+
a += BitConverter.IsLittleEndian ? Unsafe.ReadUnaligned<uint>(ref tail) : BinaryPrimitives.ReverseEndianness(Unsafe.ReadUnaligned<uint>(ref tail));
223238
break;
224239
case 3:
225-
a += ((uint)data[index + 2]) << 16;
240+
a += (uint)Unsafe.Add(ref tail, 2) << 16;
226241
goto case 2;
227242
case 2:
228-
a += ((uint)data[index + 1]) << 8;
243+
a += (uint)Unsafe.Add(ref tail, 1) << 8;
229244
goto case 1;
230245
case 1:
231-
a += (uint)data[index];
246+
a += Unsafe.Add(ref tail, 0);
232247
break;
233248
case 0:
234249
hash1 = c;

0 commit comments

Comments
 (0)