【问题标题】:Preventing JIT inlining on a method防止方法上的 JIT 内联
【发布时间】:2023-09-30 23:34:01
【问题描述】:

我遇到了一种特殊的情况。我一直在开发一个用于发送电子邮件的开源库。在这个库中,我需要一种可靠的方法来获取调用方法。我通过分析其中的StackFrame 对象来使用StackTrace 完成此操作。这在关闭优化的调试模式项目中没有问题。

当我切换到启用优化的发布模式时,会出现问题。堆栈跟踪如下所示:

> FindActionName at offset 66 in file:line:column <filename unknown>:0:0
> Email at offset 296 in file:line:column <filename unknown>:0:0
> CallingEmailFromRealControllerShouldFindMailersActionName at offset 184
     in file:line:column <filename unknown>:0:0
> _InvokeMethodFast at offset 0 in file:line:column <filename unknown>:0:0
> InvokeMethodFast at offset 152 in file:line:column <filename unknown>:0:0
...

这取自失败的单元测试。在此跟踪的第 3 行中,我应该看到一个名为 TestEmail 的方法,该方法在其他地方定义,但我相信 JITter 正在内联它。我读过您可以通过使方法虚拟化来防止内联,但这不起作用。有谁知道防止方法内联的可靠方法,以便您的方法显示在堆栈跟踪中?

【问题讨论】:

    标签: c# jit inlining


    【解决方案1】:

    您可以使用MethodImplAttribute 并指定MethodImplOptions.NoInlining

    [MethodImpl(MethodImplOptions.NoInlining)]
    void YourMethod()
    {
        // do something
    }
    

    请注意,这仍然不能保证您可以使用源代码中看到的实际调用方法。你的方法不会被内联,但你的方法的调用者可以被内联到它自己的调用者等等。

    【讨论】:

    • 这就像一个魅力!可以将它放在一个类上以防止该类中的任何方法被内联吗?
    • @Scott:据我所知,它仅适用于方法(和构造函数)。
    • 这绝对有效,但我想我需要找到一种方法在课堂上做类似的事情。我不想强迫我的消费者把它放在每个 EmailResult 方法上。如果消费者从您的基类继承,是否可以在他们调用实现中的方法时收到通知?
    • 我需要在一个属性上使用它,我可以确认它也可以放在属性的 getter 或 setter 之上,因为它们实际上被编译为方法。
    • 它只适用于方法,因为只有方法可以内联。基于方法的通知更多的是基于 AOP 或日志记录的解决方案。
    【解决方案2】:

    您可以使用标有System.Runtime.CompilerServices.CallerMemberNameAttribute 的附加参数,它是CallerFilePathCallerLineNumber 的兄弟。如果我理解正确,这应该为您提供正确的方法名称,无论内联的是什么,什么不是。你只会得到方法名,但是我看不到任何东西可以得到类名/程序集等。

    不言而喻,但只是为了确定......这样的东西不应该在日志/诊断之外使用。

    这样做的正确方法可能是:

    我意识到,经过这么长时间,这可能对 Scott 没有任何帮助,但也许其他人可以从中受益。

    【讨论】:

    • 注意:如果您可以依赖 .Net 4.5 或更高版本,这肯定是一个干净的解决方案。
    最近更新 更多