Skip to content

Commit f7c990e

Browse files
committed
perf(runtime): remove callback count allocation
1 parent 1c7e0e1 commit f7c990e

3 files changed

Lines changed: 26 additions & 2 deletions

File tree

src/Orleans.Core/Messaging/StripedCallbackDictionary.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ public int Count
113113
/// Counts items matching a predicate across all stripes.
114114
/// </summary>
115115
public int CountWhere(Func<TValue, bool> predicate)
116+
=> CountWhere(predicate, static (value, predicate) => predicate(value));
117+
118+
/// <summary>
119+
/// Counts items matching a predicate across all stripes.
120+
/// </summary>
121+
public int CountWhere<TState>(TState state, Func<TValue, TState, bool> predicate)
116122
{
117123
int count = 0;
118124
foreach (var stripe in _stripes)
@@ -121,7 +127,7 @@ public int CountWhere(Func<TValue, bool> predicate)
121127
{
122128
foreach (var value in stripe.Dictionary.Values)
123129
{
124-
if (predicate(value))
130+
if (predicate(value, state))
125131
{
126132
count++;
127133
}

src/Orleans.Runtime/Core/InsideRuntimeClient.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,9 @@ public void Participate(ISiloLifecycle lifecycle)
616616
}
617617

618618
public int GetRunningRequestsCount(GrainInterfaceType grainInterfaceType)
619-
=> this.callbacks.CountWhere(c => c.Message.InterfaceType == grainInterfaceType);
619+
=> this.callbacks.CountWhere(
620+
grainInterfaceType,
621+
static (callback, grainInterfaceType) => callback.Message.InterfaceType == grainInterfaceType);
620622

621623
private async Task MonitorCallbackExpiry()
622624
{

test/Orleans.Runtime.Tests/StripedCallbackDictionaryTests.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace Tester;
99
public class StripedCallbackDictionaryTests
1010
{
1111
private static readonly Action<int, object?> EmptyVisitor = static (_, _) => { };
12+
private static readonly Func<int, int, bool> MatchValue = static (value, expected) => value == expected;
1213

1314
[Fact]
1415
public void CorrelationIdsDistributeAcrossStripesAtOverflowAndWithStride()
@@ -142,4 +143,19 @@ public void EmptyVisitorDoesNotAllocate()
142143

143144
Assert.Equal(0, allocated);
144145
}
146+
147+
[Fact]
148+
public void StatefulCountDoesNotAllocate()
149+
{
150+
var dictionary = new StripedCallbackDictionary<int>();
151+
Assert.True(dictionary.TryAdd(new CorrelationId(42), 42));
152+
Assert.Equal(1, dictionary.CountWhere(42, MatchValue));
153+
154+
var allocatedBefore = GC.GetAllocatedBytesForCurrentThread();
155+
var count = dictionary.CountWhere(42, MatchValue);
156+
var allocated = GC.GetAllocatedBytesForCurrentThread() - allocatedBefore;
157+
158+
Assert.Equal(1, count);
159+
Assert.Equal(0, allocated);
160+
}
145161
}

0 commit comments

Comments
 (0)