Constexpr 与模板元编程 - 性能差异
Constexpr vs Template Metaprogramming - performance differences
我可以看到 constexpr 正在赢得越来越多的领域,其使用模板元编程 (TMP) 的理由之一是性能。我的问题是:
有这方面性能对比的例子吗?
如果 TMP 更好,为什么不用 constexpr 替换它呢?
TMP 保证它是 运行 编译时,但这也可以用 constexpr 来完成。 Constexpr 处理浮点数,而 TMP 不处理浮点数,对于我见过的所有示例,替换 TMP 的 constexpr 函数更容易阅读和使用更少的代码行。 constexpr 还允许条件和循环,而 TMP 仅处理循环的递归 "simulations"。
我之前在这里 () 询问过一个相关问题 - 我有 3 种不同的元编程方式 - 这里首选 constexpr 函数方式..
constexpr
函数通常更具可读性,因为它们是常规函数。
注意它可以在运行时使用,即使有编译时常量参数(在调用站点)。
constexpr int f(int n) { /*...*/ }
constexpr int f5 = f(5); // Compile time.
int f4 = f(4); // Runtime time (compiler can optimize as for non-constexpr functions though)
对于运行时性能,在常量表达式中使用时也有类似的情况,因为都是在编译时完成的。
对于编译时性能,TMP 的问题是实例化的数量,而 constexpr
函数(带有常规参数)通常使用较少的实例化,因此 TMP 通常使编译时间更长。
但是,如果需要记忆(对于递归实现的斐波那契数列)
constexpr std::size_t fibonacci(std::size_t n)
{
if (n < 2) return 1;
return fibonacci(n - 1) + fibonacci(n - 2);
}
template <std::size_t N> struct Fibonacci
{
constexpr std::size_t value = Fibonacci<N - 2>::value + Fibonacci<N - 1>::value;
};
template <> struct Fibonacci<0>
{
constexpr std::size_t value = 1;
};
template <> struct Fibonacci<1>
{
constexpr std::size_t value = 1;
};
TMP 应该重用实例化,而 constexpr
函数可能每次都重新计算它。在那种情况下,TMP 将编译得更快。
And why not replace TMP with constexpr if it is better?
首先,我们不会放弃使用的 TMP,以实现追溯兼容性。
那么TMP一般更适应return种或者几种。
template <typename T> struct add_pointer
{
using type = T*;
};
using int_ptr = add_pointer<int>::type;
而 constexpr 需要 decltype
and/or std::declval
template <typename T> struct Tag { using type = T; };
constexpr Tag<T*> add_pointer(Tag<T>) {return {}; }
using int_ptr = decltype(add_pointer(Tag<int>{})::type;
我可以看到 constexpr 正在赢得越来越多的领域,其使用模板元编程 (TMP) 的理由之一是性能。我的问题是:
有这方面性能对比的例子吗?
如果 TMP 更好,为什么不用 constexpr 替换它呢?
TMP 保证它是 运行 编译时,但这也可以用 constexpr 来完成。 Constexpr 处理浮点数,而 TMP 不处理浮点数,对于我见过的所有示例,替换 TMP 的 constexpr 函数更容易阅读和使用更少的代码行。 constexpr 还允许条件和循环,而 TMP 仅处理循环的递归 "simulations"。
我之前在这里 (
constexpr
函数通常更具可读性,因为它们是常规函数。
注意它可以在运行时使用,即使有编译时常量参数(在调用站点)。
constexpr int f(int n) { /*...*/ }
constexpr int f5 = f(5); // Compile time.
int f4 = f(4); // Runtime time (compiler can optimize as for non-constexpr functions though)
对于运行时性能,在常量表达式中使用时也有类似的情况,因为都是在编译时完成的。
对于编译时性能,TMP 的问题是实例化的数量,而 constexpr
函数(带有常规参数)通常使用较少的实例化,因此 TMP 通常使编译时间更长。
但是,如果需要记忆(对于递归实现的斐波那契数列)
constexpr std::size_t fibonacci(std::size_t n)
{
if (n < 2) return 1;
return fibonacci(n - 1) + fibonacci(n - 2);
}
template <std::size_t N> struct Fibonacci
{
constexpr std::size_t value = Fibonacci<N - 2>::value + Fibonacci<N - 1>::value;
};
template <> struct Fibonacci<0>
{
constexpr std::size_t value = 1;
};
template <> struct Fibonacci<1>
{
constexpr std::size_t value = 1;
};
TMP 应该重用实例化,而 constexpr
函数可能每次都重新计算它。在那种情况下,TMP 将编译得更快。
And why not replace TMP with constexpr if it is better?
首先,我们不会放弃使用的 TMP,以实现追溯兼容性。
那么TMP一般更适应return种或者几种。
template <typename T> struct add_pointer
{
using type = T*;
};
using int_ptr = add_pointer<int>::type;
而 constexpr 需要 decltype
and/or std::declval
template <typename T> struct Tag { using type = T; };
constexpr Tag<T*> add_pointer(Tag<T>) {return {}; }
using int_ptr = decltype(add_pointer(Tag<int>{})::type;