我的编译器会忽略无用的代码吗?

我已经通过关于这个主题的networking几个问题,但我没有find任何答案我的问题,或者它是另一种语言或它不完全回答 (死代码是不是无用的代码)所以这里是我的问题:

编译器忽略了(显式的还是不是)无用的代码?

例如,在这个代码中:

double[] TestRunTime = SomeFunctionThatReturnDoubles; // A bit of code skipped int i = 0; for (int j = 0; j < TestRunTime.Length; j++) { } double prevSpec_OilCons = 0; 

将for循环删除?

我使用.net4.5和vs2013


背景是我保留了很多代码(我没有写) ,我想知道无用的代码应该是一个目标,还是让我的编译器负责。

那么,你的variablesiprevSpec_OilCons ,如果不使用任何地方将被优化,但不是你的循环。

所以如果你的代码如下所示:

 static void Main(string[] args) { int[] TestRunTime = { 1, 2, 3 }; int i = 0; for (int j = 0; j < TestRunTime.Length; j++) { } double prevSpec_OilCons = 0; Console.WriteLine("Code end"); } 

在ILSpy下它将是:

 private static void Main(string[] args) { int[] TestRunTime = new int[] { 1, 2, 3 }; for (int i = 0; i < TestRunTime.Length; i++) { } Console.WriteLine("Code end"); } 

由于循环有几个语句,比如比较和增量,可以用来实现稍微的延迟/等待时间。 (虽然这不是一个好的做法)

考虑下面的循环,这是一个空循环,但是需要花费很多时间来执行。

 for (long j = 0; j < long.MaxValue; j++) { } 

代码中的循环不是死代码,就死代码而言,以下代码是死代码,将被优化。

 if (false) { Console.Write("Shouldn't be here"); } 

这个循环,甚至不会被.NET的抖动去除。 根据这个答案

循环不能被删除 ,代码没有死 ,例如:

  // Just some function, right? private static Double[] SomeFunctionThatReturnDoubles() { return null; } ... double[] TestRunTime = SomeFunctionThatReturnDoubles(); ... // You'll end up with exception since TestRunTime is null for (int j = 0; j < TestRunTime.Length; j++) { } ... 

通常,编译器不能预测 SomeFunctionThatReturnDoubles所有可能结果,这就是为什么它保留了循环

在你的循环中有两个操作隐含在每个迭代中。 增量:

 j++; 

和比较

 j<TestRunTime.Length; 

所以,循环不是空的,虽然看起来是这样的。 最后还有一些东西正在执行,编译器当然不会忽略它。

这也发生在其他循环中。

它不会被忽略。 然而,当你到达IL时会有一个跳转语句,所以for会像if语句一样运行。 它也会运行++和长度的代码,正如@Fleve所提到的。 这将只是额外的代码。 为了可读性,以及与代码标准保持一致,如果您不使用代码,我将删除代码。

编译器忽略了(显式的还是不是)无用的代码?

你不能轻易确定它是无用的,所以编译器也不能。 例如TestRunTime.Length的getter可能有副作用。

背景是,我维护了很多代码(我没有写),我想知道如果无用的代码应该是一个目标

在重构一段代码之前,你必须validation它的function,以便能够改变它,然后说它仍然有相同的结果。 unit testing是做这件事的好方法。

JIT基本上可以清除死码。 这不是很彻底。 死亡variables和expression式被可靠地杀死。 这是SSAforms的简单优化。

不确定控制stream量。 如果你嵌套两个循环,只有内部的一个将被删除我记得。

如果你想知道什么被删除,什么看不到生成的x86代码。 C#编译器只做了很less的优化。 JIT做了一些。

4.5 32和64位JIT是不同的代码库,具有不同的行为。 一个新的JIT(RyuJIT)即将到来,在我的testing通常会变得更糟,有时更好。

显然你的编译器不会忽略无用的代码,但仔细分析它,然后尝试删除它,如果它执行优化。

在你的情况下,第一个有趣的事情是variablesj是否在循环之后使用。 另一个有趣的事情是TestRunTime.Length。 编译器会查看它,并检查它是否总是返回相同的结果,如果是,是否有任何副作用,如果是,是否一次调用副作用总是与重复调用相同。

如果TestRunTime.Length没有副作用,j没有被使用,那么循环被移除。

否则,如果重复调用TestRunTime.Length有比一次调用更多的副作用,或者如果重复的调用返回不同的值,则必须执行循环。

否则,j = max(0,TestRunTime.Length)。

接下来,编译器可以确定是否需要分配TestRunTime.Length。 它可能会替代代码,只是确定什么TestRunTime.Length将是。

那么当然你的编译器可能不会尝试任何花哨的优化,或者语言规则可能不能确定这些东西,而且你被卡住了。

大部分情况下,您不必担心主动删除无用的代码。 如果遇到性能问题,并且你的分析器说一些无用的代码正在吃掉你的时钟周期,那么就去核算它。 但是,如果代码真的没有任何作用,也没有副作用,那么对运行时间的影响可能不大。

也就是说,大多数编译器不需要执行任何优化,所以依靠编译器优化并不总是最明智的select。 但是在很多情况下,即使是无用的旋转循环也可以执行得非常快。 循环一百万次的基本自旋锁将编译成类似mov eax, 0 \ inc eax \ cmp eax, 1000000 \ jnz -8 。 即使我们在CPU上进行了优化,但由于没有内存访问,每个循环只有3个周期(在最近的RISC芯片上),所以不会有任何caching失效。 在1GHz的CPU上,这只有300万/ 1,000,000,000秒,或3毫秒。 如果你每秒运行60次,这将是一个相当重大的打击,但在许多情况下,它可能不会引人注目。

像我所描述的那样的一个循环几乎肯定会被窥视孔优化成mov eax 1000000 ,即使在JIT环境中也是如此。 这可能会比这更进一步优化,但没有其他的背景下,这种优化是合理的,不会造成不良影响。

tl; dr:如果你的分析器说死/无用的代码使用了大量的运行时资源,那就删除它。 尽pipe如此,不要去寻找死刑的女巫。 把这个重写/大规模的重构留下来。

奖励:如果代码生成器知道eax不会被读取以外的其他任何东西,并希望保留自旋锁,它可以生成mov eax, 1000000 \ dec eax \ jnz -3并减less惩罚循环一个循环。 大多数编译器会完全删除它。

我已经做了一个小testing,根据几个answerers关于使用long.MaxValue想法,这里是我的参考代码:

 public Form1() { InitializeComponent(); Stopwatch test = new Stopwatch(); test.Start(); myTextBox.Text = test.Elapsed.ToString(); } 

这里的代码有点无用的代码:

 public Form1() { InitializeComponent(); Stopwatch test = new Stopwatch(); test.Start(); for (int i = 0; i < int.MaxValue; i++) { } myTextBox.Text = test.Elapsed.ToString(); } 

你会说,我用int.MaxValue而不是long.MaxValue ,我不想花这一天的一天。

如你看到的:

 --------------------------------------------------------------------- | | Debug | Release | --------------------------------------------------------------------- |Ref | 00:00:00.0000019 | 00:00:00.0000019 | |Useless code | 00:00:05.3837568 | 00:00:05.2728447 | --------------------------------------------------------------------- 

代码没有被优化。 稍等一下,我会尝试用一些int[]来testingint[].Lenght

 public Form1() { InitializeComponent(); int[] myTab = functionThatReturnInts(1); Stopwatch test = new Stopwatch(); test.Start(); for (int i = 0; i < myTab.Length; i++) { } myTextBox.Text = test.Elapsed.ToString(); } public int[] functionThatReturnInts(int desiredSize) { return Enumerable.Repeat(42, desiredSize).ToArray(); } 

结果如下:

 --------------------------------------------- | Size | Release | --------------------------------------------- | 1 | 00:00:00.0000015 | | 100 | 00:00:00 | | 10 000 | 00:00:00.0000035 | | 1 000 000 | 00:00:00.0003236 | | 100 000 000 | 00:00:00.0312673 | --------------------------------------------- 

所以即使是数组,它也不会被优化。