Created
March 21, 2018 02:29
-
-
Save JeremyKuhne/2b503df20e61f143341d32d8c247837f to your computer and use it in GitHub Desktop.
This file contains 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
using System; | |
using System.Buffers; | |
using System.Diagnostics; | |
using System.Reflection; | |
using System.Runtime.CompilerServices; | |
using System.Threading; | |
using System.Threading.Tasks; | |
static class PoolingBenchmark | |
{ | |
static void Main() | |
{ | |
var pools = new[] | |
{ | |
ArrayPool<byte>.Shared, | |
// ArrayPool<byte>.Create() | |
}; | |
foreach (int numBuffers in new[] { 4, 2, 1 }) | |
{ | |
foreach (int workPerBuffer in new[] { 1, 256, 4096 }) | |
{ | |
foreach (var yield in new AwaitYieldApproach[] { new NoYield(), new LocalYield(), new GlobalYield() }) | |
{ | |
foreach (ArrayPool<byte> pool in pools) | |
{ | |
Time(pool, 100000, numBuffers, workPerBuffer, yield); | |
} | |
} | |
} | |
} | |
} | |
private static async Task<int> Work(ArrayPool<byte> pool, int iters, int numBuffers, int workPerBuffer, AwaitYieldApproach yield) | |
{ | |
int sum = 0; | |
var buffers = new byte[numBuffers][]; | |
for (int iter = 0; iter < iters; iter++) | |
{ | |
for (int i = 0; i < numBuffers; i++) buffers[i] = pool.Rent(4096); | |
foreach (byte[] buffer in buffers) | |
{ | |
for (int i = 0; i < workPerBuffer; i++) buffer[i] = (byte)i; | |
await yield; | |
for (int i = 0; i < workPerBuffer; i++) sum += buffer[i]; | |
} | |
for (int i = numBuffers - 1; i >= 0; --i) pool.Return(buffers[i]); | |
} | |
return sum; | |
} | |
private abstract class AwaitYieldApproach : ICriticalNotifyCompletion | |
{ | |
public AwaitYieldApproach GetAwaiter() => this; | |
public void GetResult() { } | |
public abstract bool IsCompleted { get; } | |
public abstract void OnCompleted(Action action); | |
public void UnsafeOnCompleted(Action action) => OnCompleted(action); | |
} | |
private sealed class NoYield : AwaitYieldApproach | |
{ | |
public override bool IsCompleted => true; | |
public override void OnCompleted(Action action) { } | |
} | |
private sealed class LocalYield : AwaitYieldApproach | |
{ | |
public override bool IsCompleted => false; | |
public override void OnCompleted(Action action) => Task.Run(action); | |
} | |
private sealed class GlobalYield : AwaitYieldApproach | |
{ | |
public override bool IsCompleted => false; | |
public override void OnCompleted(Action action) => ThreadPool.QueueUserWorkItem(s => ((Action)s)(), action); | |
} | |
private static void Time(ArrayPool<byte> pool, int iters, int numBuffers, int workPerBuffer, AwaitYieldApproach yield) | |
{ | |
GC.Collect(); | |
GC.WaitForPendingFinalizers(); | |
GC.Collect(); | |
int gen0 = 0, gen1 = 0, gen2 = 0; | |
var sw = new Stopwatch(); | |
int threadCount = Environment.ProcessorCount; | |
using (var b = new Barrier(threadCount + 1)) | |
{ | |
for (int i = 0; i < threadCount; i++) | |
{ | |
new Thread(() => | |
{ | |
b.SignalAndWait(); | |
b.SignalAndWait(); | |
Work(pool, iters, numBuffers, workPerBuffer, yield).Wait(); | |
b.SignalAndWait(); | |
}).Start(); | |
} | |
b.SignalAndWait(); | |
gen0 = GC.CollectionCount(0); | |
gen1 = GC.CollectionCount(1); | |
gen2 = GC.CollectionCount(2); | |
sw.Restart(); | |
b.SignalAndWait(); | |
b.SignalAndWait(); | |
sw.Stop(); | |
gen0 = GC.CollectionCount(0) - gen0; | |
gen1 = GC.CollectionCount(1) - gen1; | |
gen2 = GC.CollectionCount(2) - gen2; | |
} | |
Console.WriteLine($"{pool.GetType().GetTypeInfo().Name,38}(#Arr:{numBuffers,-2},Work/Arr:{workPerBuffer,-4}, {yield.GetType().Name,-11}) => Time: {sw.Elapsed}, GC0/1/2: {gen0,5} / {gen1,5} / {gen2,3}"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment