在编译时计算表达式

本文关键字:表达式 计算 编译 | 更新日期: 2023-09-27 18:20:38

我知道这个问题被问了很多,但只针对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  

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

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

谢谢你的帮助,真的很感激!

在编译时计算表达式

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

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

class MyCalc
{
    private static readonly double spi = Math.Sin(Math.PI);
    private static readonly double pi = 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
    }
}

我不确定编译器是否能完全优化计算中的选项一,但它应该是最可读和最可维护的方式。

如果你想在编译时做尽可能多的事情,事情会变得更加困难。在C++下,您有模板。我觉得它们写起来很麻烦,但人们用它做了很多令人惊叹的事情。编译时函数似乎变得更容易了,但我还没有尝试过。D有CTFE,它真的很强大。但D是一个利基市场,我会避免在其中编写任何严肃的代码。我不知道其他语言有相当明确的预编译评估,但我相信也有一些。

现在的编译人员相当聪明。编译器很有可能看到在没有提示的情况下内联优化函数调用的机会。使用DotNet 4.5,我们获得了AggressiveInlining属性,因此我们可能能够迫使编译器朝着正确的方向前进。C/C++也有类似的东西,并且出现了一些问题。我方的一般建议是避免inline,直到你确切地知道自己在做什么。

如果你真的不想从C#开始走这条路,在我看来,最好的选择是使用上述功能在C++中编写你的功能,编写一个易于使用的C接口,并通过PInvoke调用它。但如果真的值得,请帮自己一个忙,并在之前衡量一下。永远不要忘记优化的两条规则:

  1. 不要
  2. 还没有(仅限专家)

没有副作用的方法有[Pure]属性。然而,这只用于代码分析,而不是由编译器(目前)使用。然而,这种情况在未来可能会改变。

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

因此,目前,你需要一个变通方法,比如你预先计算的值,最好是给别人一个注释,让别人知道值的来源:

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

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

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