Skip to content

Instantly share code, notes, and snippets.

@george-polevoy
Created September 19, 2022 17:51
Show Gist options
  • Save george-polevoy/91354e6c16b8bf0ee7cdd3eb8b260f54 to your computer and use it in GitHub Desktop.
Save george-polevoy/91354e6c16b8bf0ee7cdd3eb8b260f54 to your computer and use it in GitHub Desktop.
compiled delegate cache
using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
namespace DelegateIdentifier;
public class Tests
{
private const int n = 3;
private const int nIter = 10_000_000;
[Test]
public void TestFullMethod()
{
var list = Enumerable.Range(0, n).ToList();
var s = 0;
object prev;
var func = ExpressionCache.Get(static (List<int> _) => default(int),
static () => static l => l.SelectMany(i => Enumerable.Range(0, i).Select(j => j * 2)).Sum());
for (var i = 0; i < nIter; i++)
{
s = func(list);
}
}
[Test]
public void TestFunc_SelectMany()
{
int s = 0;
var list = Enumerable.Range(0, n).ToList();
Func<List<int>, int> expression = static l => l.SelectMany(i => Enumerable.Range(0, i).Select(j => j * 2)).Sum();
var func = expression;
for (var i = 0; i < nIter; i++)
{
s = func(list);
}
}
[Test]
public void TestCompiled_SelectMany()
{
int s = 0;
var list = Enumerable.Range(0, n).ToList();
Expression<Func<List<int>, int>> expression = static l => l.SelectMany(i => Enumerable.Range(0, i).Select(j => j * 2)).Sum();
var func = expression.Compile();
for (var i = 0; i < nIter; i++)
{
s = func(list);
}
}
[Test]
public void TestRaw_SelectMany()
{
var list = Enumerable.Range(0, n).ToList();
for (var iter = 0; iter < nIter; iter++)
{
var s = list.SelectMany(i => Enumerable.Range(0, i).Select(j => j * 2)).Sum();
}
}
[Test]
public void TestForeach()
{
var list = Enumerable.Range(0, n).ToList();
for (var iter = 0; iter < nIter; iter++)
{
var s = 0;
foreach (var i in list)
{
for (var j = 0; j < i; j++)
{
s += j * 2;
}
}
}
}
[Test]
public void TestForeachWithDelegate()
{
var list = Enumerable.Range(0, n).ToList();
Func<int> del = () =>
{
var s = 0;
foreach (var i in list)
{
for (var j = 0; j < i; j++)
{
s += j * 2;
}
}
return s;
};
var d = new ConcurrentDictionary<IntPtr, object>();
for (var i = 0; i < nIter; i++)
{
var cachedDel = d.GetOrAdd((IntPtr)0, (ptr, i) => del, del);
var val = ((Func<int>)cachedDel).Invoke();
}
}
}
public static class ExpressionCache
{
private static readonly ConcurrentDictionary<IntPtr, object?> DelegateCache = new();
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
private static IntPtr GetMethodId(Delegate del)
{
return del.Method.MethodHandle.Value;
}
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public static Func<TArg, TResult> Get<TArg, TResult>(Func<TArg, TResult> _, Func<Expression<Func<TArg, TResult>>> getter)
{
var del = DelegateCache.GetOrAdd(GetMethodId(getter), static (_, arg) =>
{
var compiled = arg().Compile();
return compiled;
}, getter);
return (Func<TArg, TResult>)del!;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment