3

我在 SO 或 Internet 上都找不到类似的问题/答案,并且与这个问题一样无用,也许这是我在阅读 MSIL 时想到的一个问题。我很想了解 IL 操作执行在我的场景中是如何工作的,即使这不是一个实际问题,我也没有人要问。

前提:

请记住,MSIL 命令和函数的执行分三个步骤完成:

  1. 将命令操作数或函数参数压入堆栈。
  2. 执行 MSIL 命令或调用函数。命令或函数从堆栈中弹出其操作数(参数)并将结果压入堆栈结果(返回值)。
  3. 从堆栈中读取结果。

步骤 1 和 3 是可选的。例如,void 函数不会将返回值压入堆栈。

我知道这是一种方法的作用,它决定了需要多少“处理能力”,但为了我的好奇心,让我们考虑这两种非常基本的方法:

第一种方法:

void Method1()
{
  var result = 1+1;
}

第二种方法:

int Method2()
{
  var result = 1+1;
  return result;
}

问题:

因为 void 方法不推送返回值(或者是否存在隐式返回),这是否意味着与第二种方法相比,它在执行时需要更少的开销?

4

2 回答 2

6

但是请记住,MSIL 不会被执行,甚至不会被解释。MSIL 是编译代码的表达式,因为它将在虚拟机上执行。但是 JIT 编译器将 MSIL 转换为机器代码(x86 等)。x86 与基于堆栈的虚拟机有着根本的不同。

在最基本的层面上,函数在寄存器中返回值。假设寄存器足够大。让我们坚持使用 64 位值(引用、长整数、双精度和更小的值类型)。这些可以在 RAX 寄存器中返回。在您的简单示例中,为这两个函数生成的机器代码没有区别。也就是说,Method1将是:

mov rax, 1
inc rax
ret

(而且,是的,我知道智能编译器会崩溃1+12. 假设那里有内存访问,好吗?)

Method2将是相同的,因为要返回的值已经在 RAX 寄存器中。

因此,当您使用 64 位或更小的数量时,返回值的方法和不返回值的方法之间的差异通常会有所不同,如果有的话,仅在加载返回值到正确的寄存器。这通常是一条mov指令。这将取决于事情完成的顺序,以及编译器和 JITer 优化事情的程度。

于 2012-08-30T16:03:06.533 回答
3

所有事情都是平等的,做某事会比不做某事慢。

但请注意,第一种方法的实现将变成:

void Method1()
{
}

内联后,它甚至没有被调用(如果这是另一个对象上的实例方法,那么调用很可能被字段的读取所取代,因为优化无法停止,NullReferenceException()如果调用它应该抛出null。如果它是静态的,从同一个对象调用,或者编译器确定该对象不能为null,那么甚至不是。零代码。

同时,第二种方法将变为:

int Method2()
{
  return 2;
}

内联后,使用该值的调用将从例如:

x = Method2();

到:

x = 2;

如果不使用该值,则甚至不使用该值;它将与第一个完全相同。

这种事情的发生意味着即使在没有内联调用的情况下,诸如是否返回某些东西之类的微小差异大部分都会丢失,因为甚至不会针对更大的因素产生噪音,并且也可能违反直觉(通过影响是否以某种模糊的方式采用某些优化路径)。示例:产生了值,通过将其放入寄存器中“返回”它,但也许在计算时它仍然存在,也许它的下一次使用直接在那里工作;因此“归还”它的成本为零。

于 2012-08-30T16:02:37.080 回答