You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#动态委托首次调用性能劣化及预调用无效问题问询

关于C#动态委托首次调用性能差异的解析

首先,咱们先明确你看到的首次调用慢、后续快的核心原因:JIT(即时编译)编译。

一、首次测试时被缓存的内容

当你第一次调用objCreators[...].Invoke(...)或propSetters[...].Invoke(...)时,CLR(公共语言运行时)需要把这些Lambda表达式对应的IL(中间语言)编译成本地机器码——这个过程是有开销的。编译完成后,本地机器码会被缓存到当前应用域中,后续再调用同一个委托实例时,就直接执行缓存的机器码,不需要再编译,所以速度会大幅提升。

你提到即使让Lambda依赖c值,首次调用后性能还是能提升,这完全符合JIT的特性:JIT编译的是方法的结构逻辑,而不是具体的参数值。只要Lambda对应的匿名方法的IL结构不变,不管传入什么参数,编译后的机器码都是通用的,所以第一次编译后,后续所有调用都能复用缓存。

二、为什么预调用没复现缓存效果?

你预调用没起作用,大概率是因为预调用没有覆盖到实际测试中用到的所有委托实例。结合你的代码来看:

  • 实际测试循环中,你是遍历inputSelections里的每个selection,调用objCreators[selection.MarketID]和propSetters[selection.SelectionID]对应的委托。
  • 如果你的预调用只是随便选几个虚拟参数调用,没有遍历inputSelections中所有出现过的MarketID和SelectionID,甚至没有遍历objCreators和propSetters的全部Key,那么那些没被预调用的委托在第一次测试循环时,还是会触发JIT编译,导致首次测试耗时依然很高。

举个例子:假设objCreators里有10个不同的MarketID对应的Func,你预调用只测了2个,剩下8个在第一次测试循环时才第一次被调用,它们的JIT编译开销还是会加到首次测试的耗时里,所以你看不到预调用的效果。

三、验证优化的方法

要让预调用真正起作用,你需要确保所有在测试中会用到的委托都被触发一次JIT编译:

// 预调用所有objCreators中的委托
foreach (var kvp in objCreators)
{
    // 传入任意符合签名的参数,比如0
    var dummyObj = kvp.Value.Invoke(0);
    // 同时预调用所有propSetters中的委托
    foreach (var setterKvp in propSetters)
    {
        setterKvp.Value.Invoke(dummyObj);
    }
}

这样就能确保所有委托对应的匿名方法都被JIT编译过,再跑测试循环时,首次调用的耗时应该会和后续测试接近。

另外补充一点:如果你的objCreators中的Lambda是创建不同类型的对象(比如new SomeObjectType()、new AnotherObjectType()),那么每个不同类型的构造方法调用也可能触发各自的JIT编译,预调用时也要确保覆盖到所有这些类型的实例化操作。

内容的提问来源于stack exchange,提问作者Kobek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:52:05