c# - Roslyn 编译器用零优化函数调用乘法

标签 c# optimization visual-studio-2015 roslyn

昨天我在我的 C# 代码中发现了这个奇怪的行为:

Stack<long> s = new Stack<long>();

s.Push(1);           // stack contains [1]
s.Push(2);           // stack contains [1|2]
s.Push(3);           // stack contains [1|2|3]

s.Push(s.Pop() * 0); // stack should contain [1|2|0]

Console.WriteLine(string.Join("|", s.Reverse()));

我假设程序会打印 1|2|0 但实际上它打印了 1|2|3|0

查看生成的 IL 代码(通过 ILSpy),您可以看到 s.Pop() * 0 被优化为简单的 0:

// ...
IL_0022: ldloc.0
IL_0023: ldc.i4.0
IL_0024: conv.i8
IL_0025: callvirt instance void class   [System]System.Collections.Generic.Stack`1<int64>::Push(!0)
// ...

ILSpy反编译:

Stack<long> s = new Stack<long>();
s.Push(1L);
s.Push(2L);
s.Push(3L);
s.Push(0L); // <- the offending line
Console.WriteLine(string.Join<long>("|", s.Reverse<long>()));

首先,我最初在 Windows 7 下使用 Visual Studio 2015 Update 3 以及 Release模式 (/optimize) 和 Debug模式以及各种目标框架(4.0、4.5、4.6 和 4.6.1)对此进行了测试).在所有 8 种情况下,结果都是相同的 (1|2|3|0)。

然后我在 Windows 7 下使用 Visual Studio 2013 Update 5 对其进行了测试(再次使用发布/ Debug模式和目标框架的所有组合)。令我惊讶的是,此处的语句优化掉并产生预期结果 1|2|0

因此我可以得出结论,此行为既不依赖于/optimize 也不依赖于目标框架标志,而是依赖于所使用的编译器版本。

出于兴趣,我用 C++ 编写了类似的代码,并使用当前的 gcc 版本对其进行了编译。这里乘以零的函数调用没有被优化掉,函数被正确执行。

我认为这种优化只有在 stack.Pop() 是一个纯函数(它肯定不是)时才有效。但我不太愿意将此称为错误,我认为这只是我不知道的一个功能?

这个“功能”是否有任何记录,是否有一种(简单的)方法来禁用此优化?

最佳答案

是的,这绝对是一个错误。 * 0,如果有副作用,不应该优化成0。

感谢您报告问题!!

您可以在以下位置跟踪错误/修复的进度 https://github.com/dotnet/roslyn/issues/13486

关于c# - Roslyn 编译器用零优化函数调用乘法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39254676/

相关文章:

c# - 从透明形式的图像中删除轮廓

asp.net-mvc - 在 Visual Studio 2015 RC 中添加 View

c# - 注册到事件的命名方法

C++ 分析/优化 : How to get better profiling granularity in an optimized function

python - 使用许多属性和字典查找优化 Python 代码

python - 向量化或优化循环,其中每次迭代都取决于前一次迭代的状态

clr - asp.net mvc 6如何知道 Controller 不继承自Controller类时要添加哪些资源?

c# - 创建 XSD 以验证 C# 标志枚举

c# - "extends itself"确保什么的接口(interface)?

c# - 构建错误: The type or namespace name 'TweetSharp' could not be found