【问题标题】:How to check if a parameter of an inline function is known at compile time?如何在编译时检查内联函数的参数是否已知?
【发布时间】:2011-06-28 15:51:21
【问题描述】:

我有一个性能关键的内联函数。它根据参数生成一些数据。我希望编译器优化所有调用的数据生成,其中参数在编译时是已知的。问题是我不能强制编译器将优化后的数据从堆栈中放入静态常量,因为标记数据static 会破坏参数不是编译时常量的情况。在堆栈上拥有不变的数据会损害性能。有没有办法推断(可能使用模板/boost::enable_if),参数是编译时常量并选择数据生成的适当实现?

澄清

基本上我有以下内容:

struct Data {
     int d_[16];
};

inline Data fun(int param)
{  //param can sometimes be a compile-time constant

    ... //generate the data
    Data res = {gen0, gen2, gen3, ..., gen15}; //put the data into result
    return res;
}

所以当param 不是编译时常量时,我​​们只是生成所有数据并返回。
param 已知时,编译器可以优化数据生成。但随后它未能优化以下行并生成大量代码,只是将res 成员设置为已知数据(数据嵌入到程序代码中)。我希望编译器创建一个静态常量,然后将其复制到返回对象(这比使用嵌入数据执行大量代码要快)。由于这是一个内联函数,因此甚至可能不需要复制。

免责声明

这个问题和How to use different overload of an inline function, depending on a compile time parameter?不一样。这是一个更普遍的问题。

【问题讨论】:

  • 我不认为有办法检测函数参数是否是编译时间常数,我不明白你为什么重命名旧问题,所以标题与问题相矛盾并开始了一个新问题一个在这个。
  • 不,新标题与问题不矛盾。我在编辑中错误地表达了自己,直到现在才意识到这一点。谢谢!

标签: c++ static constants inline


【解决方案1】:

这不是可移植的,但在 GCC 和可能的 Clang 上有一个 __builtin_constant_p 编译器函数。这使您可以询问编译器是否在编译期间知道变量的值。你可以这样使用它:

void f(int arg) {
  if (__builtin_constant_p(arg) && arg == 0) {
    // Handle case where arg is 0 AND known at compile time.
  } else {
    // Generic code.
  }
}

这样,如果arg 在编译时已知并且为0,编译器将不会在else 分支中生成代码。

让这个更便携的一个有用技巧可能是使用一些宏hackery。

#ifdef __GNUC__
#  define CONSTANT_P(x) __builtin_constant_p(x)
#else
#  define CONSTANT_P(x) 0
#endif

根据需要添加支持与此类似的其他编译器,您现在可以在不支持此功能的编译器上使用它而无需额外开销。那就是那些编译器,如果它们有什么价值的话,将消除CONSTANT_P 分支,只留下通用代码。

【讨论】:

    【解决方案2】:

    您是否确实分析了您的代码并证明将常量传递给您的(内联?)函数是瓶颈?

    如果您确实进行了分析,那么您将不得不帮助编译器解决这个问题,因为没有办法自动完成。当您知道常量和普通版本时,您必须手动调用函数的模板版本。

    【讨论】:

    • 优化后的函数速度提高了 20%(整个函数,而不是加载阶段)。似乎如果将大常量作为立即数嵌入到代码中(请参阅我的编辑),则每次都会重新生成整个常量,即使我不需要所有数据。
    【解决方案3】:

    如果函数是内联的,那么编译器在内联函数时在适当的地方执行常量折叠优化,假设你有一个相当合理的编译器。

    【讨论】:

      【解决方案4】:

      听起来你有:

      template <int N> myfunc_const_N() { /*...*/ }
      inline myfunc_var_N(int N);
      

      并且您希望能够键入myfunc(n); 并让编译器调用myfunc_const_N&lt;n&gt;();(如果有效)或myfunc_var_N(n);(如果无效)?

      我的猜测是不可能的,但这是一件很难证明的事情。

      但是如果可以的话,它真的会让你受益匪浅吗?在编写代码时,您多久知道给定表达式是否是编译时常量?如果有常量,为什么不自己使用模板版本,如果没有,为什么不使用函数参数版本?

      【讨论】:

      • 嵌入式系统经常会出现这种情况。例如,在某些 ST 微控制器上,一些端口寄存器将引脚 0 映射到位 0,引脚 1 映射到位 1,引脚 2 映射到位 2,等等。其他的将引脚 0 映射到位 0-1,引脚 1 到位 2-3,引脚 2 到位 4-5 等。如果想要对一组 I/O 引脚执行操作,这些 I/O 引脚由使用“pin n=bit n”掩码计算的掩码定义,则可能必须生成带有位的掩码“扩散;传播开”。如果掩码是一个常量,最好的方法是对扩展进行编码,表达式计算为编译时常量。但是,如果掩码不是恒定的,则...
      • ...评估表达式所需的代码真的很糟糕,最好调用函数调用来进行转换。
      【解决方案5】:

      我不相信有任何方法可以做到这一点;优化调用是编译器的责任,而不是语言的……所以没有可移植的方法来做到这一点。 :\

      【讨论】:

        猜你喜欢
        • 2017-05-01
        • 1970-01-01
        • 2013-09-19
        • 2013-09-29
        • 2020-05-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多