MS C#编译器和非优化代码

Chr*_*rna 15 .net c# compiler-construction optimization cil

注意:我在发布的示例中发现了一些错误 - 编辑修复它

如果不启用优化,官方C#编译器会做一些有趣的事情.

例如,一个简单的if语句:

int x;
// ... //
if (x == 10)
   // do something
Run Code Online (Sandbox Code Playgroud)

如果优化,会变成以下内容:

ldloc.0
ldc.i4.s 10
ceq
bne.un.s do_not_do_something
// do something
do_not_do_something:
Run Code Online (Sandbox Code Playgroud)

但如果我们禁用优化,它会变成这样:

ldloc.0
ldc.i4.s 10
ceq
ldc.i4.0
ceq
stloc.1
ldloc.1
brtrue.s do_not_do_something
// do something
do_not_do_something:
Run Code Online (Sandbox Code Playgroud)

我无法理解这一点.为什么所有额外的代码,似乎在源中不存在?在C#中,这相当于:

int x, y;
// ... //
y = x == 10;
if (y != 0)
   // do something
Run Code Online (Sandbox Code Playgroud)

有谁知道为什么这样做?

Eri*_*ert 21

我不完全理解问题的重点.听起来你问"为什么编译器在优化开关关闭时产生未经优化的代码?" 有点回答自己.

但是,我会捅它.我认为问题实际上就是"什么样的设计决策会导致编译器发出声明,存储和加载本地#1,哪些可以被优化掉?"

答案是因为未经优化的codegen被设计为清晰,明确,易于调试,并鼓励抖动生成不会积极收集垃圾的代码.我们实现所有这些目标的方法之一是为堆栈中的大多数值生成局部化,甚至是临时值.我们来看一个更复杂的例子.假设你有:

Foo(Bar(123), 456)
Run Code Online (Sandbox Code Playgroud)

我们可以将其生成为:

push 123
call Bar - this pops the 123 and pushes the result of Bar
push 456
call Foo
Run Code Online (Sandbox Code Playgroud)

这是好的,有效的和小的,但它不符合我们的目标.它清晰明确,但调试起来并不容易,因为垃圾收集器可能会变得激进.如果Foo由于某种原因实际上没有对其第一个参数执行任何操作,则允许GC在Foo运行之前回收Bar的返回值.

在未经优化的构建中,我们将生成更像的东西

push 123
call Bar - this pops the 123 and pushes the result of Bar
store the top of the stack in a temporary location - this pops the stack, and we need it back, so
push the value in the temporary location back onto the stack
push 456
call Foo
Run Code Online (Sandbox Code Playgroud)

现在,抖动有一个很大的暗示,即"嘿抖动,即使Foo不使用它,也要在当地保持活力一段时间 "

这里的一般规则是"在未优化的构建中使所有临时值中的局部变量".你去吧; 为了评估"if"语句,我们需要评估一个条件并将其转换为bool.(当然条件不需要是bool类型;它可以是一个可以隐式转换为bool的类型,或者是一个实现操作符true/operator false对的类型.)未经优化的代码生成器被告知"积极地转换所有临时值进入当地人",这就是你得到的.

我想在这种情况下,我们可以压制那些在"if"语句中的条件,但这听起来像是为我工作而没有客户利益.因为只要你的手臂确实有客户利益,我就有一堆工作,我不打算改变未经优化的代码生成器,它生成未经优化的代码,完全按照预期的方式生成.