如何检查内联函数的参数是否在编译时已知?

5
我有一个性能关键的内联函数。它基于参数生成一些数据。我希望编译器优化所有已知参数在编译时的调用中的数据生成。问题是,我无法强制编译器将优化后的数据从堆栈移到静态常量,因为标记数据为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成员设置为已知数据(数据嵌入到程序代码中)。我希望编译器创建一个静态常量,然后将其复制到返回对象中(这比执行嵌入数据的大量代码更快)。由于这是一个内联函数,即使复制也可能是不必要的。

免责声明

这个问题与如何根据编译时参数使用不同的内联函数重载?不同。 这是一个更通用的问题。


我认为没有办法检测函数参数是否为编译时常量,而且我不明白你为什么要重命名旧问题以使标题与问题相矛盾,并在此基础上开始一个新问题。 - UncleBens
不,新标题并不与问题相矛盾。我在编辑时表达不正确,直到现在才意识到。谢谢! - user283145
5个回答

2

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


1

所以听起来你有:

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

如果您想要输入myfunc(n);并让编译器调用myfunc_const_N<n>();(如果有效)或者myfunc_var_N(n);(如果无效),那么这是可能的吗?

我猜这是不可能的,但是证明这一点很困难。

但是,如果您真的能做到这一点,您会获得多少好处呢?在编写代码时,您有多少次不知道给定表达式是否为编译时常量?如果您确实有一个常量,请自己使用模板版本,如果没有,请使用函数参数版本。


这种情况在嵌入式系统中经常出现。例如,在某些ST微控制器上,一些端口寄存器将引脚0映射到位0,引脚1映射到位1,引脚2映射到位2等等。其他的则将引脚0映射到位0-1,引脚1映射到位2-3,引脚2映射到位4-5等等。如果想要对由“引脚n=位n”掩码定义的一组I/O引脚执行操作,则可能需要生成一个“分散”的位掩码。如果该掩码是一个常量,则最好编写一个表达式来扩展它,以便在编译时计算为常量。但是,如果该掩码不是常量,则... - supercat
需要评估表达式的代码将非常糟糕,最好调用函数来进行转换。 - supercat

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分支的代码。

使其更具可移植性的一个有用技巧可能是使用一些宏技巧。

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

如有需要,请添加其他支持类似功能的编译器,现在您可以在不增加额外开销的情况下使用它,这些编译器(如果值得一提)将消除CONSTANT_P分支,留下通用代码。


1
如果函数被内联,编译器会在适当的时候执行常量折叠优化,假设你使用的是相当合理的编译器。

1
你是否实际对代码进行了分析并证明将常量传递给你的(内联?)函数是瓶颈所在?
如果你已经进行了分析,那么你需要帮助编译器解决这个问题,因为没有自动完成的方法。当你知道常量时,必须手动调用函数的模板版本,否则调用普通版本。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接