通过缓存元函数优化编译时性能

Lou*_*nne 13 c++ templates instantiation boost-mpl template-meta-programming

假设我有以下元函数:

template <typename T>
struct make_pair {
    using type = std::pair<
        typename std::remove_reference<T>::type,
        typename std::remove_reference<T>::type
    >;
};
Run Code Online (Sandbox Code Playgroud)

相反,它会提高编译速度(或其他)吗?

template <typename T>
struct make_pair {
    using without_reference = typename std::remove_reference<T>::type;
    using type = std::pair<without_reference, without_reference>;
};
Run Code Online (Sandbox Code Playgroud)

我看到两种可能性:

  1. 编译器每次看到它都必须做一些工作typename std::remove_reference<T>::type.使用中间别名具有某种"缓存"行为,这允许编译器只执行一次工作.

  2. 编译时性能是根据编译器必须执行的模板实例化的数量来衡量的.因为std::remove_reference<T>::type引用的类型相同std::remove_reference<T>::type,所以在这两种情况下只需要一个模板实例化,因此两个实现都是等效的WRT编译时性能.

我认为B是对的,但我想确定一下.如果答案结果是编译器特定的,我最感兴趣的是知道Clang和GCC的答案.

编辑:

我对测试程序的编译进行了基准测试,以便使用一些数据.测试程序做了类似的事情:

template <typename ...> struct result;    

template <typename T>
struct with_cache {
    using without_reference = typename std::remove_reference<T>::type;
    using type = result<without_reference, ..., without_reference>;
};

template <typename T>
struct without_cache {
    using type = result<
        typename std::remove_reference<T>::type,
        ...,
        typename std::remove_reference<T>::type
    >;
{ };

using Result = with[out]_cache<int>::type;
Run Code Online (Sandbox Code Playgroud)

这些是10个程序编译的平均时间,其中有10 000个模板参数result<>.

                -------------------------
                | g++ 4.8 | clang++ 3.2 |
-----------------------------------------
| with cache    | 0.1628s | 0.3036s     |
-----------------------------------------
| without cache | 0.1573s | 0.3785s     |
-----------------------------------------
Run Code Online (Sandbox Code Playgroud)

测试程序由此处提供的脚本生成.

Jak*_*ake 2

我不能说所有编译器都是如此,但 GCC 以及很可能所有其他主要编译器都会使用记忆化。如果你仔细想想,那几乎是必须的。

考虑下面的代码

&f<X, Y>::some_value == &f<X, Y>::some_value
Run Code Online (Sandbox Code Playgroud)

这必须是正确的,因此编译器必须确保它不会重复方法和静态成员的定义。现在可能还有其他方法可以做到这一点,但这对我来说只是尖叫记忆;我什至没有看到另一种方法来实现这一点(当然,我已经非常努力地考虑过)

当我使用 TMP 时,我希望发生记忆化。如果不这样做,那将是一个真正的痛苦,太慢了。我发现编译时性能存在重大差异的唯一方法是 a) 使用更快的编译器,如 Clang(比 GCC 快 3 倍)并选择不同的算法。根据我的经验,在我看来,小的常数因素在 TMP 中的重要性甚至比在 C 或 C++ 中的重要性还要小。选择正确的算法,尽量不要做不必要的工作,尽量减少实例化的数量,并使用好的编译器(MSVC++ 确实很,而且离 C++11 合规性很远,但 GCC 和 Clang 相当不错);这就是你真正能做的。

此外,您应该始终牺牲编译时间来获得更好的代码。过早的编译时优化比普通的过早优化要邪恶得多。如果由于某种原因性能变得严重阻碍开发,则可能存在例外情况;不过我还没有听说过这样的案例。