Last active
June 29, 2018 07:00
-
-
Save dadhi/091e7a1edcb2cc6e657b54fb7ba1a22d to your computer and use it in GitHub Desktop.
Simple object pool implementations benchmarked C# .NET
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
StackPool is faster by far >10 times than ScanPool. | |
Other features: | |
- Does not consume memory from the start - it grows only when you Return object to it. | |
- Forgiving for no Return scenarios, that means when you not returning - memory is not consumed. So it is similar to `new` | |
- May be put a limit on depth, NOT tested yet. | |
*/ | |
using System.Runtime.CompilerServices; | |
using System.Threading; | |
using BenchmarkDotNet.Attributes; | |
namespace Playground | |
{ | |
[MemoryDiagnoser, DisassemblyDiagnoser] | |
public class ObjectPoolComparison | |
{ | |
private static readonly ScanPool<X> _scanNoScanPool = new ScanPool<X>(0); | |
private static readonly ScanPool<X> _scanPool = new ScanPool<X>(); | |
private static readonly StackPool<X> _stackPool = new StackPool<X>(); | |
[Benchmark(Baseline = true)] | |
public int ScanPool_NoScan_2Rents2Returns() | |
{ | |
var p = _scanNoScanPool; | |
var x1 = RentNoScanOrNew(p, true, 55, "55"); | |
var x2 = RentNoScanOrNew(p, true, 66, "66"); | |
p.ReturnNoScan(x1); | |
p.ReturnNoScan(x2); | |
var x3 = RentNoScanOrNew(p, true, 77, "77"); | |
var x4 = RentNoScanOrNew(p, true, 77, "77"); | |
return x3.I + x4.I; | |
} | |
[Benchmark] | |
public int ScanPool_2Rents2Returns() | |
{ | |
var p = _scanPool; | |
var x1 = RentOrNew(p, true, 55, "55"); | |
var x2 = RentOrNew(p, true, 66, "66"); | |
p.Return(x1); | |
p.Return(x2); | |
var x3 = RentOrNew(p, true, 77, "77"); | |
var x4 = RentOrNew(p, true, 77, "77"); | |
return x3.I + x4.I; | |
} | |
[Benchmark] | |
public int StackPool_2Rents2Returns() | |
{ | |
var p = _stackPool; | |
var x1 = RentOrNew(p, true, 55, "55"); | |
var x2 = RentOrNew(p, true, 66, "66"); | |
p.Return(x1); | |
p.Return(x2); | |
var x3 = RentOrNew(p, true, 77, "77"); | |
var x4 = RentOrNew(p, true, 77, "77"); | |
return x3.I + x4.I; | |
} | |
public static X RentNoScanOrNew(ScanPool<X> p, bool b, int i, string s) => | |
p.RentNoScan()?.Init(b, i, s) ?? new X(b, i, s); | |
public static X RentOrNew(ScanPool<X> p, bool b, int i, string s) => | |
p.Rent()?.Init(b, i, s) ?? new X(b, i, s); | |
public static X RentOrNew(StackPool<X> p, bool b, int i, string s) => | |
p.Rent()?.Init(b, i, s) ?? new X(b, i, s); | |
public class X | |
{ | |
public bool B; | |
public int I; | |
public string S; | |
public X(bool b, int i, string s) | |
{ | |
B = b; I = i; S = s; | |
} | |
public X Init(bool b, int i, string s) | |
{ | |
B = b; I = i; S = s; | |
return this; | |
} | |
} | |
} | |
public sealed class StackPool<T> where T : class | |
{ | |
public T Rent() => | |
Interlocked.Exchange(ref _s, _s?.Tail)?.Head; | |
public void Return(T x) => | |
Interlocked.Exchange(ref _s, new Stack(x, _s)); | |
private Stack _s; | |
private sealed class Stack | |
{ | |
public readonly T Head; | |
public readonly Stack Tail; | |
public Stack(T h, Stack t) | |
{ | |
Head = h; | |
Tail = t; | |
} | |
} | |
} | |
public sealed class ScanPool<T> where T : class | |
{ | |
private T _x; | |
private readonly T[] _xs; | |
public ScanPool(int n = 32) { _xs = new T[n]; } | |
public T RentNoScan() => Interlocked.Exchange(ref _x, null); | |
public T Rent() => | |
Interlocked.Exchange(ref _x, null) ?? | |
ScanGet(); | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
private T ScanGet() | |
{ | |
T x = null; | |
var xs = _xs; | |
for (var i = 0; i < xs.Length && | |
(x = Interlocked.Exchange(ref xs[i], null)) == null; | |
++i) {} | |
return x; | |
} | |
public void ReturnNoScan(T x) => Interlocked.Exchange(ref _x, x); | |
public void Return(T x) | |
{ | |
if (Interlocked.CompareExchange(ref _x, x, null) != null) | |
ScanSet(x); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
private void ScanSet(T x) | |
{ | |
var xs = _xs; | |
for (var i = 0; i < xs.Length && | |
Interlocked.CompareExchange(ref xs[i], x, null) != null; | |
++i) {} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment