Last active
July 20, 2022 11:35
Star
You must be signed in to star a gist
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.Linq; | |
using System.Runtime.CompilerServices; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using BenchmarkDotNet.Attributes; | |
using BenchmarkDotNet.Jobs; | |
namespace Benchs.Tricks; | |
[SimpleJob(RuntimeMoniker.Net60)] | |
[SimpleJob(RuntimeMoniker.NetCoreApp31)] | |
[SimpleJob(RuntimeMoniker.Net461)] | |
[MarkdownExporterAttribute.GitHub] | |
[MeanColumn] | |
[MemoryDiagnoser] | |
public class ClosureBenchmark | |
{ | |
[Params(10, 100)] public int TaskCount { get; set; } | |
[Benchmark(Baseline = true)] | |
public int AutoClosure() | |
{ | |
for (var repeat = 0; repeat < Repeats; repeat++) | |
{ | |
for (var i = 0; i < _objects.Length; i++) | |
{ | |
var obj = _objects[i]; | |
var handler = _handlers[i]; | |
_tasks[i] = Task.Run(() => handler.Handle(in obj)); | |
} | |
Task.WaitAll(_tasks); | |
} | |
return _value; | |
} | |
[Benchmark] | |
public int ParallelFor() | |
{ | |
for (var repeat = 0; repeat < Repeats; repeat++) | |
{ | |
Parallel.For(0, _objects.Length, i => _handlers[i].Handle(in _objects[i])); | |
} | |
return _value; | |
} | |
[Benchmark] | |
public int ParallelForeach() | |
{ | |
for (var repeat = 0; repeat < Repeats; repeat++) | |
{ | |
for (var i = 0; i < _parallelClosures.Length; i++) | |
{ | |
ref var parallelClosure = ref _parallelClosures[i]; | |
parallelClosure.Value = _objects[i]; | |
} | |
Parallel.ForEach(_parallelClosures, block => block.Handle()); | |
for (var i = 0; i < _parallelClosures.Length; i++) | |
{ | |
ref var parallelClosure = ref _parallelClosures[i]; | |
parallelClosure.Value = default; | |
} | |
} | |
return _value; | |
} | |
[Benchmark] | |
public int SelfClosure() | |
{ | |
for (var repeat = 0; repeat < Repeats; repeat++) | |
{ | |
for (var i = 0; i < _objects.Length; i++) | |
{ | |
_tasks[i] = Task.Run(_closures[i].Prepare(in _objects[i])); | |
} | |
Task.WaitAll(_tasks); | |
foreach (var closure in _closures) | |
{ | |
closure.Clear(); | |
} | |
} | |
return _value; | |
} | |
[Benchmark] | |
public int TaskFactory() | |
{ | |
for (var repeat = 0; repeat < Repeats; repeat++) | |
{ | |
for (var i = 0; i < _objects.Length; i++) | |
{ | |
_taskFactoryClosures[i].Value = _objects[i]; | |
_tasks[i] = Task.Factory.StartNew(_doWork, | |
_taskFactoryClosures[i], | |
CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); | |
} | |
Task.WaitAll(_tasks); | |
foreach (var closure in _taskFactoryClosures) | |
{ | |
closure.Value = default; | |
} | |
} | |
return _value; | |
} | |
#region Configuration | |
private const int Repeats = 1000; | |
private Closure[] _closures = null!; | |
private Action<object?> _doWork = null!; | |
private Fly[] _objects = null!; | |
private Handler[] _handlers = null!; | |
private ParallelClosure[] _parallelClosures = null!; | |
private Task[] _tasks = null!; | |
private TaskFactoryClosure[] _taskFactoryClosures = null!; | |
private int _value; | |
[GlobalSetup] | |
public void Init() | |
{ | |
ExecuteDelegate execute = Execute; | |
_closures = Enumerable.Range(0, TaskCount).Select(_ => new Closure(execute)).ToArray(); | |
_doWork = DoWork!; | |
_handlers = Enumerable.Range(0, TaskCount).Select(_ => new Handler(execute)).ToArray(); | |
_objects = Enumerable.Range(0, TaskCount).Select(i => new Fly { FirstValue = i, SecondValue = i }).ToArray(); | |
_tasks = new Task[TaskCount]; | |
_taskFactoryClosures = new TaskFactoryClosure[TaskCount]; | |
_value = int.MinValue; | |
_parallelClosures = new ParallelClosure[TaskCount]; | |
for (var i = 0; i < _handlers.Length; i++) | |
{ | |
var handler = _handlers[i]; | |
_parallelClosures[i] = new ParallelClosure(handler); | |
_taskFactoryClosures[i] = new TaskFactoryClosure(handler); | |
} | |
} | |
#endregion | |
private static void DoWork(object value) => ((TaskFactoryClosure)value).Handle(); | |
private void Execute(in Fly value) => _value += value.FirstValue; | |
private sealed class Closure | |
{ | |
private readonly ExecuteDelegate _action; | |
private readonly Action _closure; | |
private Fly _value; | |
public Closure(ExecuteDelegate action) | |
{ | |
_action = action; | |
_closure = Execute; | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public void Clear() => _value = default; | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public Action Prepare(in Fly value) | |
{ | |
_value = value; | |
return _closure; | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
private void Execute() => _action(in _value); | |
} | |
private delegate void ExecuteDelegate(in Fly value); | |
private struct Fly | |
{ | |
public int FirstValue; | |
public int SecondValue; | |
} | |
private sealed class Handler | |
{ | |
private readonly ExecuteDelegate _closure; | |
public Handler(ExecuteDelegate closure) | |
{ | |
_closure = closure; | |
} | |
public void Handle(in Fly value) => _closure(in value); | |
} | |
private struct ParallelClosure | |
{ | |
private readonly Handler _handler; | |
public Fly Value; | |
public ParallelClosure(Handler handler) | |
{ | |
_handler = handler; | |
Value = default; | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public void Handle() => _handler.Handle(in Value); | |
} | |
private sealed class TaskFactoryClosure | |
{ | |
private readonly Handler _handler; | |
public Fly Value; | |
public TaskFactoryClosure(Handler handler) | |
{ | |
_handler = handler; | |
Value = default; | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public void Handle() => _handler.Handle(in Value); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment