C# 准时制;循环优化 使用系统; 命名空间控制台应用程序1 { 课堂测试数学 { 静态void Main() { 双精度=0.0; 对于(int i=0;i

C# 准时制;循环优化 使用系统; 命名空间控制台应用程序1 { 课堂测试数学 { 静态void Main() { 双精度=0.0; 对于(int i=0;i,c#,optimization,jit,C#,Optimization,Jit,要执行您想要的优化,编译器必须确保函数Sqrt()将始终为特定输入返回相同的值 编译器可以进行各种检查,确保函数没有使用任何其他“外部”变量来查看它是否是无状态的。但这也并不总是意味着它不会受到副作用的影响 当一个函数在循环中被调用时,它应该在每次迭代中被调用(想想一个多线程环境,看看为什么这很重要)。因此,如果用户想要这种优化,通常要由用户从循环中取出常量 回到C++编译器——编译器可能对库函数有一定的优化。很多编译器试图优化像数学库这样的重要库,这样编译器可能是特定的。 另外一个大的区别是C

要执行您想要的优化,编译器必须确保函数
Sqrt()
将始终为特定输入返回相同的值

编译器可以进行各种检查,确保函数没有使用任何其他“外部”变量来查看它是否是无状态的。但这也并不总是意味着它不会受到副作用的影响

当一个函数在循环中被调用时,它应该在每次迭代中被调用(想想一个多线程环境,看看为什么这很重要)。因此,如果用户想要这种优化,通常要由用户从循环中取出常量

回到C++编译器——编译器可能对库函数有一定的优化。很多编译器试图优化像数学库这样的重要库,这样编译器可能是特定的。

另外一个大的区别是C++中通常包含头文件中的一些东西。这意味着编译器可能有所有的信息来决定函数调用在调用之间没有变化。 Net编译器(在编译时-Visual Studio)并不总是有所有要解析的代码。大多数库函数都已编译(到IL-第一阶段)。因此,考虑到第三方DLL,可能无法进行深度优化。在JIT(运行时)编译跨程序集进行此类优化的成本可能太高。

这可能有助于JIT(甚至C编译器)如果
Math.Sqrt
被注释为。那么,假设函数的参数与示例中的参数一样是常量,则可以将该值的计算提升到循环之外

此外,这样的循环可以合理地转换为代码:

using System; 

namespace ConsoleApplication1
{ 
    class TestMath
    {  
        static void Main()
        {
            double res = 0.0;

            for(int i =0;i<1000000;++i)
                res +=  System.Math.Sqrt(2.0);

            Console.WriteLine(res);

            Console.ReadKey();  
        }
    }
}
理论上,编译器或JIT可以自动执行这项操作,但我怀疑它会针对实际代码中很少出现的模式进行优化


我打开了一个,表示设计时工具暗示了这样的重构。

我重新开始,我在1.2毫秒时将C++版本时钟,12.2秒的C版本。如果你看机器代码,C++代码生成器和优化器发出的原因很容易被看到。它像这样重写循环(使用C等价):

double temp=Math.Sqrt(2.0);
对于(int i=0;i<1000000;++i){
res+=温度;
}

这是两种优化的组合,称为“不变代码运动”和“循环提升”,换句话说,C++编译器对SQL RTS-()有足够的了解。函数知道其返回值不受周围代码的影响,因此可以随意移动。然后,将该代码移到循环之外并创建一个额外的局部变量来存储结果是值得的。以及比添加速度慢。听起来很明显,但这是一条必须内置到优化器中的规则,并且必须加以考虑,这是许多规则中的一条

是的,抖动优化器忽略了一个。它是无法花费与C++优化器相同的时间量的,它在很长的时间约束下运行。因为如果它花费太长,那么程序花费太多的时间开始。


开玩笑:一个C#程序员需要比代码生成器聪明一点,并且自己认识到这些优化机会。这是一个相当明显的机会。好吧,现在你已经知道了:)

我看不出JIT编译器有什么理由不能做到这一点。当前版本可能会,也可能不会,但这并不意味着未来的版本不会。我很好奇-您是否尝试过在发布模式下编译,而不是在调试模式下编译?您可能会发现发布模式大大提高了性能。@ChrisShain您确定编译器可以做到这一点吗?它确实可以必须确定数学。Sqrt没有副作用,所以它可以将其减少到一次调用。数字对我来说是不正确的。C版本运行时间长10倍,但调用次数是一百万倍?@John:不,这只是一个教育目的代码。不是生产代码,如果这是你所说的“实际”的意思的话。除了使用发布版本外,还应确保未连接调试器(即直接在visual studio外部启动exe)为什么编译器需要知道计算sqrt比添加慢?另外,C编译器不能执行这个提升,而不是将它留给JIT吗?好吧,从另一个角度来解释。如果sqrt比添加快,那么这将是一个糟糕的优化。C编译器不优化代码,这是一个抖动任务。与C++编译器,它是一个代码生成器。我必须丢失一些东西,因为我看不出如何避免N个加法运算。改变的是执行一个Sqt而不是n。如果n=1,那么可能有额外的登记变量是慢的,但是如果我们假设n> 1,那么即使被提升的操作本身也是一个加法运算,我们仍然会。提前说出来,对吧?不确定你在说什么。如果你建议把重复的加法变成乘法(可能在纸上,res=1000000*temp)那么,不,这不是一个有效的优化。由于舍入错误,浮点数学没有关联性,你会得到不同的结果。你当然可以自己这样写。这不是我试图提出的。我试图绘制的图片涉及用,比如说,
1.0+2.0
替换
sqrt(2.0)
(具有固定值的加法运算)。您说过,
sqrt
比加法慢很重要。因此,如果
sqrt
的成本与加法相同,编译器会有什么不同?我不明白为什么您关于相对成本的陈述会有任何不同。
double res = 1000000 * Math.Sqrt(2.0);
double temp = Math.Sqrt(2.0);
for (int i = 0; i < 1000000; ++i) {
    res += temp;
}