Dre*_*kes 11 .net c# generics delegates dynamic-invoke
这个问题部分是关于代表,部分是关于泛型.
鉴于简化的代码:
internal sealed class TypeDispatchProcessor
{
private readonly Dictionary<Type, Delegate> _actionByType
= new Dictionary<Type, Delegate>();
public void RegisterProcedure<T>(Action<T> action)
{
_actionByType[typeof(T)] = action;
}
public void ProcessItem(object item)
{
Delegate action;
if (_actionByType.TryGetValue(item.GetType(), out action))
{
// Can this call to DynamicInvoke be avoided?
action.DynamicInvoke(item);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我在其他地方读到,直接调用委托(带括号)比调用快几个数量级DynamicInvoke
,这是有道理的.
对于上面的代码示例,我想知道我是否可以执行类型检查并以某种方式提高性能.
一些上下文:我有一个对象流,可以在各种处理程序中运行,这些处理程序可以在运行时注册/取消注册.上面的模式完全符合我的目的,但如果可能的话,我想让它变得更加快捷.
一种选择是存储Action<object>
在Dictionary
,并Action<T>
用另一个委托包装代理.我还没有比较第二次间接调用会影响的性能变化.
Jon*_*eet 23
我强烈怀疑打包电话会比使用电话更有效率DynamicInvoke
.您的代码将是:
internal sealed class TypeDispatchProcessor
{
private readonly Dictionary<Type, Action<object>> _actionByType
= new Dictionary<Type, Action<object>>();
public void RegisterProcedure<T>(Action<T> action)
{
_actionByType[typeof(T)] = item => action((T) item);
}
public void ProcessItem(object item)
{
Action<object> action;
if (_actionByType.TryGetValue(item.GetType(), out action))
{
action(item);
}
}
}
Run Code Online (Sandbox Code Playgroud)
值得对它进行基准测试,但我认为你会发现它更有效率.DynamicInvoke
必须使用反射等检查所有参数,而不是包装的委托中的简单强制转换.
所以我做了一些测量.
var delegates = new List<Delegate>();
var actions = new List<Action<object>>();
const int dataCount = 100;
const int loopCount = 10000;
for (int i = 0; i < dataCount; i++)
{
Action<int> a = d => { };
delegates.Add(a);
actions.Add(o => a((int)o));
}
var sw = Stopwatch.StartNew();
for (int i = 0; i < loopCount; i++)
{
foreach (var action in actions)
action(i);
}
Console.Out.WriteLine("{0:#,##0} Action<object> calls in {1:#,##0.###} ms",
loopCount * dataCount, sw.Elapsed.TotalMilliseconds);
sw = Stopwatch.StartNew();
for (int i = 0; i < loopCount; i++)
{
foreach (var del in delegates)
del.DynamicInvoke(i);
}
Console.Out.WriteLine("{0:#,##0} DynamicInvoke calls in {1:#,##0.###} ms",
loopCount * dataCount, sw.Elapsed.TotalMilliseconds);
Run Code Online (Sandbox Code Playgroud)
我创建了许多项来间接调用,以避免JIT可能执行的任何优化.
结果非常引人注目!
1,000,000 Action calls in 47.172 ms 1,000,000 Delegate.DynamicInvoke calls in 12,035.943 ms 1,000,000 Action calls in 44.686 ms 1,000,000 Delegate.DynamicInvoke calls in 12,318.846 ms
因此,在这种情况下,将呼叫替换DynamicInvoke
为额外的间接呼叫和强制转换大约快270倍.所有在一天的工作.