【问题标题】:How to have "constexpr and runtime" alias如何拥有“constexpr 和运行时”别名
【发布时间】:2014-01-21 00:34:51
【问题描述】:

Consexpr 对于编译优化非常有用。比如……

strlen(char*)

可以使用....进行预编译

constexpr inline size_t strlen_constexpr(char* baseChar) {
    return (
            ( baseChar[0] == 0 )
            ?(// if {
              0
              )// }
            :(// else {
              strlen_constexpr( baseChar+1 ) + 1 
              )// }
            );
}

优化后它的运行时成本为 "0"...但运行时慢了 10+x 以上

// Test results ran on a 2010 macbook air
--------- strlen ---------
Time took for 100,000 runs:1054us.
Avg Time took for 1 run: 0.01054us.
--------- strlen_constexpr ---------
Time took for 100,000 runs:19098us.
Avg Time took for 1 run: 0.19098us.

是否有任何现有的宏/模板黑客可以使用单个统一功能来代替。即。

constexpr size_t strlen_smart(char* baseChar) {
    #if constexpr
    ... constexpr function
    #else its runtime
    ... runtime function
}

或一些允许以下内容的重载黑客

constexpr size_t strlen_smart(char* baseChar) {
    ... constexpr function
}

inline size_t strlen_smart(char* baseChar) {
    ... runtime function
}

注意:这个问题一般适用于这个概念。有 2 个单独的函数用于运行时和 constexpr,而不是给出的示例函数。

免责声明:将编译器设置为 -O3(优化级别)足以修复 99.9% 的静态 char 优化,从而使上述所有示例“毫无意义”。但这与这个问题无关,因为它适用于其他“示例”,而不仅仅是strlen

【问题讨论】:

  • 请记住,即使一个函数是constexprinline,传递的参数是常量,并且函数结果作为常量有效,该函数可能仍然 i> 如果在不需要常量表达式的上下文中使用,则在运行时执行。 (从技术上讲,它也可以在运行时在需要常量表达式的上下文中执行,但这不是有用的编译器所做的事情。)您不希望在运行时调用慢版本,即使参数是常量。
  • @hvd 我从示例中删除了“const”以避免混淆。同样是的,选择优化(如果可能)仍然取决于“编译器”。然而,关键是要确保最有效的变体在运行时运行。同时仍然有利于编译器优化。
  • 我明白,但这不是我的意思。我的意思只是警告你,如果你尝试任何相当于is_constant(expr) ? strlen_const(expr) : strlen_runtime(expr) 的东西,这不会阻止strlen_const(expr) 在运行时被调用。
  • baseChar+sizeof(char) -- 不,只是没有。
  • C++ 标准演进工作组的 Here's a document 讨论了类似问题并提出了几种解决方法(通过语言扩展或更改)。但是,据我了解,C++1y 中constexpr 函数的放宽应该已经大大减少了这个问题。

标签: c++ optimization c++11 compiler-optimization


【解决方案1】:

我不知道任何通用的方法,但我知道有两种可能的具体情况。

部分编译器的具体情况

gcc 和复制 gcc 所有功能的 clang 也有一个内置函数 __builtin_constant_p。我不确定 gcc 是否会正确地将内联函数的参数视为常量,但我担心您必须从宏中使用它:

#define strlen_smart(s) \
    (__builtin_constant_p(s) && __builtin_constant_p(*s) ? \
        strlen_constexpr(s) : \
        strlen(s))

可能有用。请注意,我正在测试 s*s 的 constexpr,因为指向静态缓冲区的指针 一个编译时间常数,而它的长度是 不是

奖励:文字的具体情况(不是实际答案)

对于strlen 的特定转换,您可以使用字符串文字不是 类型为const char * 而是类型为const char[N] 的事实,它隐式转换为const char *。但它也会转换为 const char (&)[N],而 const char * 不会。

所以你可以定义:

template <size_t N>
constexpr size_t strlen_smart(const char (&array)[N])

(加上显然strlen_smartconst char *转发到strlen

即使在 C++98 中,我有时也会使用带有这种类型参数的函数,其定义对应于(我没有尝试重载 strlen 本身,但重载是为了避免调用它):

template <size_t N>
size_t strlen_smart(const char (&)[N]) { return N - 1; }

这有问题

char buffer[10] = { 0 };

strlen_smart(buffer);

应该说 0,但优化后的变体只说 9。在这样的缓冲区上调用函数没有意义,所以我不在乎。

【讨论】:

  • 哇,这是一个巧妙的技巧。不是在寻找“cstring”特定的黑客。 (因为问题更多是针对一个通用概念)。但它是我学到的新东西=) 至于编译器的具体情况,对于 windows,我们只差 1 个。我们可以结束这个问题。
  • 使用 GCC,你的strlen_smart 在不优化时总是调用strlen,但是编译器知道如何优化strlen,所以它仍然可以用常量替换它。然而,在优化时,我在 -O 处看到对 strlen_constexpr 的调用。在 -O2 或 -O3 处,这些调用被内联,但不会被常量替换。测试用例:int main() { return strlen_smart("Hello, world!"); }。这正是我在该问题的 cmets 中警告过的。
  • 顺便说一句,使用 clang,我在优化时确实看到了一个常量,但在不优化时我看到了对 strlen_constexpr 的调用。
  • @hvd 我实际上为 -O3 优化添加了免责声明。因为我觉得它超出了问题的范围。或者,有没有更好更简单的例子是 O3 无法优化的?
  • @hvd 是的,这正是 vanilla constexpr 的问题。因此,要解决这个问题,以确保发生正确的切换。 (或者至少不是低效的 constexpr)。正如 DyP 评论的那样,这正在讨论下一个标准。正如这个答案状态(第一部分),一个非理想的解决方法。
猜你喜欢
  • 2016-01-17
  • 1970-01-01
  • 2012-06-13
  • 1970-01-01
  • 2012-08-19
  • 2015-11-30
  • 2017-05-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多