Skip to content

Instantly share code, notes, and snippets.

@teoadal
Last active July 20, 2022 11:35
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save teoadal/8d71015c47aaf5348acc8a12950b9cbe to your computer and use it in GitHub Desktop.
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