在编译时评估表达式

时间:2015-11-04 09:45:38

标签: c# visual-studio compiler-construction

我知道这已被问过很多,但仅限于C / C ++和Java。 问题与使用常量表达式的性能优势有关:

当我调用只有常量作为参数的静态函数时,有没有办法告诉编译器它应该在编译时评估调用并用结果替换调用?

示例:

const double pi = Math.PI; //works as Math.PI is a constant  
const double spi = Math.Sin(Math.PI); //compiler error, because expression must be constant  

是否没有指令(更好:属性)明确地告诉编译器像Math.Sin()这样的静态方法没有在内部修改或读取任何数据,因此技术上可以在编译时评估调用?

哦,请不要回答#34;只做const double spi = 0" :),因为我的例子只是我所遇到的问题的简化版本:在保持最高性能的同时提高代码可维护性。

感谢您的帮助 - 非常感谢!

2 个答案:

答案 0 :(得分:4)

对于数值常数,我看到两个选项:

选项一:使用静态只读(在启动时计算一次):

class MyCalc
{
    private static readonly double spi = Math.Sin(Math.PI);
    private static readonly double pi = Math.Sin(Math.PI);

    public void Execute()
    {
        // .. whatever
    }
}

选项二:使用袖珍计算器执行计算并对这些常量进行硬编码:

class MyCalc
{
    // Math.Sin(Math.Pi)
    private const double spi = 0;
    // Math.Pi
    private const double pi = 3.141592653589793;

    public void Execute()
    {
        // .. whatever
    }
}

我不确定,如果编译器可以在计算中完全优化选项1,但它应该是最易读和可维护的方式。

如果您希望尽可能多地在编译时进行操作,事情会变得更加困难。在C ++下你有模板。我觉得写起来很麻烦,但人们会amazing things完成它。使用compile time functions似乎更容易,但我还没有尝试过。 D有CTFE这真的很强大。但D是一个利基,我会避免写任何严重的代码。我不知道其他语言有相当明确的预编译评估,但我确信有一些。

编译器现在已经退出智能。很可能编译器可能会在没有提示的情况下内联优化函数调用。使用DotNet 4.5,我们得到了AggressiveInlining - 属性,因此我们可以强制编译器进入正确的方向。 C / C ++有类似的东西,有problems。我的一般建议是避免inline直到你确切知道自己在做什么。

如果你真的不想从C#走这条路,那么我眼中最好的选择是使用上面提到的功能用C ++编写你的功能,编写一个易于使用的C接口并通过{{3 }}。但如果真的值得,请自己帮个忙。永远不要忘记两个优化规则:

  1. 不要
  2. 尚未(仅限专家)

答案 1 :(得分:2)

方法的[Pure]属性没有副作用。但是,这仅用于代码分析,而不是编译器(目前)。但是,这可能在将来发生变化。

JetBrains ReSharper为同样的目的(代码分析)提供了类似的[Pure]属性。

因此,目前,您需要一种解决方法,例如您预先计算的值,最好是评论其他人知道价值来源:

const double spi = 0.0; // Math.Sin(Math.PI)

static readonly double spi = Math.Sin(Math.PI);

当然会计算运行时的值,这是你不想要的。