我的编译器会忽略无用的代码吗?
我已经通过关于这个主题的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
背景是我保留了很多代码(我没有写) ,我想知道无用的代码应该是一个目标,还是让我的编译器负责。
那么,你的variablesi
和prevSpec_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 | ---------------------------------------------
所以即使是数组,它也不会被优化。