.NET如何优化返回Structure值的方法?

时间:2015-06-18 00:07:53

标签: .net vb.net

考虑一个返回Structure的方法,如下所示:

Public Function DoWork() As MyStructure

    Return New MyStructure(1.5, 1.7, 1.1, 55.9)

End Function

在这种情况下,.NET是否创建并初始化MyStructure一次,或两次

编辑:我的预感是,对DoWork的调用必须涉及.NET从一开始就推送堆栈上的返回值。否则,Return如何将任何内容返回给调用代码?这就是我所说的第一次初始化。

第二次初始化将在Return语句中,其中参数 1.5,1.7,1.1,55.9 初始化新的MyStructure值。在Return上,.NET将使用新的返回值覆盖Stack上的现有值。

问题是,我对.NET的工作方式知之甚少。我对Stacks如何工作的概念是基于在DOS下,在80年代早期尝试在Pascal中编码的模糊回忆。我不知道.NET / Windows如何做这些事情!

2 个答案:

答案 0 :(得分:5)

只需查看生成的机器代码即可查看发生的情况。您首先需要更改选项以确保启用优化程序,工具>选项>调试>一般>解开"抑制JIT优化"复选框。切换到发布版本。在DoWork调用上设置断点,当它命中时使用Debug> Windows>反汇编以查看生成的机器代码。

关于你所看到的,我会面糊一点。你的直觉是正确的,只返回简单的标量值是有效的,它们适合CPU寄存器。不是这种结构,调用方法必须在其堆栈帧上保留空间,以便被调用者可以在那里存储结构。它传递指向该保留空间的指针。换句话说,如果您将此方法声明为Public Sub DoWork(ByRef retval As MyStructure),则没有真正的区别。

只有通过内联函数才能优化此代码的其他方式。 x86抖动不会这样做,它对返回结构的方法很挑剔。事实上,x64抖动实际上是内联方法,但随后它完成了一个绝对可怕的工作。它仍然保留返回值的空间,然后生成许多不必要的初始化和值移动代码。非常残忍。这个抖动是为VS2015(项目名称RyuJIT)重写的,我还没有安装它,看看它是否做得更好。

绘制的基本结论:此代码未经优化。传递ByVal时,结构运行良好。不要让这个抽筋你的风格,我们在这里说纳秒。

答案 1 :(得分:0)

看看在IL中为BCL类型的结构做了什么,它似乎只创建了一次 Size.Truncate的示例

public static Size Truncate(SizeF value)
{
    return new Size((int)value.Width, (int)value.Height);
}
.method public hidebysig static 
valuetype System.Drawing.Size Truncate (
    valuetype System.Drawing.SizeF 'value'
) cil managed 
{
    // Method begins at RVA 0x169d6
    // Code size 22 (0x16)
    .maxstack 8

    IL_0000: ldarga.s 'value'
    IL_0002: call instance float32 System.Drawing.SizeF::get_Width()
    IL_0007: conv.i4
    IL_0008: ldarga.s 'value'
    IL_000a: call instance float32 System.Drawing.SizeF::get_Height()
    IL_000f: conv.i4
    IL_0010: newobj instance void System.Drawing.Size::.ctor(int32, int32)
    IL_0015: ret
} // end of method Size::Truncate

您可以在IL_0010 newobj操作码和documentation州注明:

  

创建一个新对象或值类型的新实例,将对象引用(类型O)推送到评估堆栈。