Skip to content

Commit 906aef7

Browse files
ReubenBondCopilot
andcommitted
Optimize object pools by replacing ThreadLocal<T> with ThreadStatic
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 1fde981 commit 906aef7

1 file changed

Lines changed: 31 additions & 3 deletions

File tree

src/Orleans.Serialization/Invocation/Pools/ConcurrentObjectPool.cs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
using System;
12
using System.Collections.Generic;
3+
using System.Runtime.CompilerServices;
24
using System.Threading;
35
using Microsoft.Extensions.ObjectPool;
46

@@ -14,17 +16,36 @@ public ConcurrentObjectPool() : base(new())
1416

1517
internal class ConcurrentObjectPool<T, TPoolPolicy> : ObjectPool<T> where T : class where TPoolPolicy : IPooledObjectPolicy<T>
1618
{
17-
private readonly ThreadLocal<Stack<T>> _objects = new(() => new());
19+
private static int NextPoolId = -1;
1820

21+
private readonly int _poolId = Interlocked.Increment(ref NextPoolId);
1922
private readonly TPoolPolicy _policy;
2023

2124
public ConcurrentObjectPool(TPoolPolicy policy) => _policy = policy;
2225

2326
public int MaxPoolSize { get; set; } = int.MaxValue;
2427

28+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
29+
private Stack<T> GetStack()
30+
{
31+
var poolId = _poolId;
32+
var stacks = PerThreadStack.Stacks;
33+
if (stacks is null)
34+
{
35+
stacks = PerThreadStack.Stacks = new Stack<T>[poolId + 1];
36+
}
37+
else if ((uint)poolId >= (uint)stacks.Length)
38+
{
39+
Array.Resize(ref stacks, Math.Max(poolId + 1, stacks.Length * 2));
40+
PerThreadStack.Stacks = stacks;
41+
}
42+
43+
return stacks[poolId] ??= new();
44+
}
45+
2546
public override T Get()
2647
{
27-
var stack = _objects.Value;
48+
var stack = GetStack();
2849
if (stack.TryPop(out var result))
2950
{
3051
return result;
@@ -37,12 +58,19 @@ public override void Return(T obj)
3758
{
3859
if (_policy.Return(obj))
3960
{
40-
var stack = _objects.Value;
61+
var stack = GetStack();
4162
if (stack.Count < MaxPoolSize)
4263
{
4364
stack.Push(obj);
4465
}
4566
}
4667
}
68+
69+
// Thread-static stacks are indexed by pool instance to avoid sharing state between pools with different policies.
70+
private static class PerThreadStack
71+
{
72+
[ThreadStatic]
73+
internal static Stack<T>[] Stacks;
74+
}
4775
}
4876
}

0 commit comments

Comments
 (0)