NLog导致类变量访问速度骤降?原因及是否为预期行为?
这个800倍性能差的原因分析
哇,这个性能差异确实夸张,我来帮你拆解一下——这绝对不是NLog导致类变量访问本身变慢,而是初始化时机的锅,加上早期版本的适配开销被误测了!
1. 静态Logger的初始化时机踩了陷阱
你把_logger定义成static readonly,它的初始化是在类第一次被访问时触发的(也就是.NET的静态构造函数阶段)。当你第一次访问s1的时候,刚好触发了LogManager.GetCurrentClassLogger()的执行——而这个方法在NLog 4.5.3里可不是简单的new一个对象:
- 它会加载NLog的配置文件(不管是
nlog.config还是appsettings.json里的配置) - 通过反射获取当前类的完整类型信息,用来命名Logger实例
- 初始化NLog的内部日志管理器、注册Logger实例
- 还可能提前初始化日志目标(比如文件输出、控制台输出)
这些都是一次性的初始化开销,但如果你的性能测试刚好把第一次访问s1的时间算进去,那整个初始化的耗时都会被算到s1的访问时间里,看起来就像是变量访问慢了800倍——实际上是你把NLog的启动开销误当成了变量访问的开销。
2. 早期NLog与.NET Core 2的适配开销
.NET Core 2是比较早期的跨平台版本,NLog 4.5.3虽然支持它,但在配置加载和类型解析上的实现不如后续版本优化。比如早期的NLog在.NET Core下读取配置需要处理更多兼容性逻辑,反射操作的开销也比.NET Framework下更明显,这进一步放大了初始化的耗时。
3. 如何验证并解决这个问题
你可以用这几个方法验证我的猜测:
- 提前初始化Logger:在性能测试代码的最开头,先手动调用一次
LogManager.GetCurrentClassLogger(),然后再测s1和s2的速度,你会发现两者的差异又回到正常的2倍左右。 - 显式静态构造函数:把Logger的初始化放到显式的静态构造函数里,提前完成初始化:
class Program { private static readonly Logger _logger; // 显式静态构造函数,提前初始化Logger static Program() { _logger = LogManager.GetCurrentClassLogger(); } // 你的其他类变量和测试代码... } - 用性能探查器定位:用Visual Studio的性能探查器跑一遍,你会发现耗时几乎全在NLog的初始化方法里,而不是类变量的访问操作上。
结论
这个现象不属于NLog的预期行为,只是因为静态Logger的初始化时机和测试逻辑重叠,加上早期版本的适配开销,导致了看起来夸张的性能差异。只要把Logger的初始化提前到测试开始前,这个问题就会消失。
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

