Skip to content

Commit 2f0af65

Browse files
authored
[sdk-metrics] Tags perf improvements (#5457)
1 parent 040012b commit 2f0af65

File tree

1 file changed

+83
-33
lines changed

1 file changed

+83
-33
lines changed

src/OpenTelemetry/Metrics/Tags.cs

+83-33
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
// Copyright The OpenTelemetry Authors
22
// SPDX-License-Identifier: Apache-2.0
33

4+
using System.Diagnostics;
5+
using System.Runtime.CompilerServices;
6+
#if NET6_0_OR_GREATER
7+
using System.Runtime.InteropServices;
8+
#endif
9+
410
namespace OpenTelemetry.Metrics;
511

612
internal readonly struct Tags : IEquatable<Tags>
@@ -12,31 +18,7 @@ namespace OpenTelemetry.Metrics;
1218
public Tags(KeyValuePair<string, object?>[] keyValuePairs)
1319
{
1420
this.KeyValuePairs = keyValuePairs;
15-
16-
#if NET6_0_OR_GREATER
17-
HashCode hashCode = default;
18-
for (int i = 0; i < this.KeyValuePairs.Length; i++)
19-
{
20-
ref var item = ref this.KeyValuePairs[i];
21-
hashCode.Add(item.Key);
22-
hashCode.Add(item.Value);
23-
}
24-
25-
var hash = hashCode.ToHashCode();
26-
#else
27-
var hash = 17;
28-
for (int i = 0; i < this.KeyValuePairs.Length; i++)
29-
{
30-
ref var item = ref this.KeyValuePairs[i];
31-
unchecked
32-
{
33-
hash = (hash * 31) + (item.Key?.GetHashCode() ?? 0);
34-
hash = (hash * 31) + (item.Value?.GetHashCode() ?? 0);
35-
}
36-
}
37-
#endif
38-
39-
this.hashCode = hash;
21+
this.hashCode = ComputeHashCode(keyValuePairs);
4022
}
4123

4224
public readonly KeyValuePair<string, object?>[] KeyValuePairs { get; }
@@ -52,33 +34,101 @@ public override readonly bool Equals(object? obj)
5234

5335
public readonly bool Equals(Tags other)
5436
{
55-
var length = this.KeyValuePairs.Length;
37+
var ourKvps = this.KeyValuePairs;
38+
var theirKvps = other.KeyValuePairs;
5639

57-
if (length != other.KeyValuePairs.Length)
40+
var length = ourKvps.Length;
41+
42+
if (length != theirKvps.Length)
5843
{
5944
return false;
6045
}
6146

47+
#if NET6_0_OR_GREATER
48+
// Note: This loop uses unsafe code (pointers) to elide bounds checks on
49+
// two arrays we know to be of equal length.
50+
if (length > 0)
51+
{
52+
ref var ours = ref MemoryMarshal.GetArrayDataReference(ourKvps);
53+
ref var theirs = ref MemoryMarshal.GetArrayDataReference(theirKvps);
54+
while (true)
55+
{
56+
// Note: string.Equals performs an ordinal comparison
57+
if (!ours.Key.Equals(theirs.Key))
58+
{
59+
return false;
60+
}
61+
62+
if (!ours.Value?.Equals(theirs.Value) ?? theirs.Value != null)
63+
{
64+
return false;
65+
}
66+
67+
if (--length == 0)
68+
{
69+
break;
70+
}
71+
72+
ours = ref Unsafe.Add(ref ours, 1);
73+
theirs = ref Unsafe.Add(ref theirs, 1);
74+
}
75+
}
76+
#else
6277
for (int i = 0; i < length; i++)
6378
{
64-
ref var left = ref this.KeyValuePairs[i];
65-
ref var right = ref other.KeyValuePairs[i];
79+
ref var ours = ref ourKvps[i];
80+
81+
// Note: Bounds check happens here for theirKvps element access
82+
ref var theirs = ref theirKvps[i];
6683

67-
// Equality check for Keys
68-
if (!left.Key.Equals(right.Key, StringComparison.Ordinal))
84+
// Note: string.Equals performs an ordinal comparison
85+
if (!ours.Key.Equals(theirs.Key))
6986
{
7087
return false;
7188
}
7289

73-
// Equality check for Values
74-
if (!left.Value?.Equals(right.Value) ?? right.Value != null)
90+
if (!ours.Value?.Equals(theirs.Value) ?? theirs.Value != null)
7591
{
7692
return false;
7793
}
7894
}
95+
#endif
7996

8097
return true;
8198
}
8299

83100
public override readonly int GetHashCode() => this.hashCode;
101+
102+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
103+
private static int ComputeHashCode(KeyValuePair<string, object?>[] keyValuePairs)
104+
{
105+
Debug.Assert(keyValuePairs != null, "keyValuePairs was null");
106+
107+
#if NET6_0_OR_GREATER
108+
HashCode hashCode = default;
109+
110+
for (int i = 0; i < keyValuePairs.Length; i++)
111+
{
112+
ref var item = ref keyValuePairs[i];
113+
hashCode.Add(item.Key.GetHashCode());
114+
hashCode.Add(item.Value);
115+
}
116+
117+
return hashCode.ToHashCode();
118+
#else
119+
var hash = 17;
120+
121+
for (int i = 0; i < keyValuePairs!.Length; i++)
122+
{
123+
ref var item = ref keyValuePairs[i];
124+
unchecked
125+
{
126+
hash = (hash * 31) + item.Key.GetHashCode();
127+
hash = (hash * 31) + (item.Value?.GetHashCode() ?? 0);
128+
}
129+
}
130+
131+
return hash;
132+
#endif
133+
}
84134
}

0 commit comments

Comments
 (0)