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

时间:2013-06-20 00:35:25

标签: c++ templates instantiation template-meta-programming boost-mpl

假设我有以下元函数:

template <typename T>
struct make_pair {
    using type = std::pair<
        typename std::remove_reference<T>::type,
        typename std::remove_reference<T>::type
    >;
};

它会改善编译速度吗(或其他东西)吗?

template <typename T>
struct make_pair {
    using without_reference = typename std::remove_reference<T>::type;
    using type = std::pair<without_reference, without_reference>;
};

我看到两种可能性:

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

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

  3. 我认为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;
    

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

                    -------------------------
                    | g++ 4.8 | clang++ 3.2 |
    -----------------------------------------
    | with cache    | 0.1628s | 0.3036s     |
    -----------------------------------------
    | without cache | 0.1573s | 0.3785s     |
    -----------------------------------------
    

    测试程序由可用脚本here生成。

1 个答案:

答案 0 :(得分:2)

我不能说所有编译器都是如此,但GCC,很可能是所有其他主要的编译器,都会使用memoization。如果你考虑一下,它几乎必须这样做。

考虑以下代码

&f<X, Y>::some_value == &f<X, Y>::some_value

这必须是真的,因此编译器必须确保它不会复制方法和静态成员的定义。现在可能有其他方法可以做到这一点,但这只是给我带来了回忆!我没有看到另一种方法来实现这一点(授予,我已经非常努力地考虑过了)

当我使用TMP时,我希望发生记忆。如果它没有,那将是一个真正的痛苦,太慢。我看到编译时性能的主要差异的唯一方法是:a)使用像Clang这样的更快的编译器(比GCC快3倍)并选择不同的算法。在我看来,TMP中的小常数因素在我的经验中比在C或C ++中更重要。选择正确的算法,尝试不做不必要的工作,尝试保持实例化的数量,并使用一个好的编译器(MSVC ++ 真的慢,远离C ++ 11合规,但GCC和Clang是相当不错);这就是你能做的一切。

此外,您应该总是牺牲编译时间来获得更好的代码。过早的编译时间优化比简单的过早优化更加邪恶。如果由于某种原因,性能变得严重不允许开发,可能会有一个例外;然而,我从来没有听说过这种情况。