【问题标题】:What are common culprits for TMP slownessTMP 缓慢的常见罪魁祸首是什么
【发布时间】:2015-04-20 20:26:29
【问题描述】:

我有一个项目使用了相当多的 C++ 模板元编程。这使得编译时间变长。我知道我不能吃蛋糕也不能吃,但我想知道一些关于如何减少编译时间的提示和技巧。我已经尝试过显式实例化,虽然这在某些情况下可能会有所帮助,但很多时候,实例对于特定的编译单元是唯一的,在这种情况下,显式实例化没有任何帮助。现在我们只讨论 Clang,它做得很好。当我在 G++ 上尝试这个时,编译时间会爆炸。对于一个文件,我放弃等待它在 45 分钟后编译。

  • 在模板元编程方面是否有任何常见的罪魁祸首,众所周知,这些问题经常出现问题?我应该避免哪些技术,应该怎么做?
  • 是否已知 GCC 的性能比 Clang 差,有什么方法可以解决这个问题?

我主要使用普通的 C++11 技术,我不使用 Boost MPL 或类似的库。

【问题讨论】:

    标签: c++ gcc clang template-meta-programming


    【解决方案1】:

    以下是有关使用 C++11 及更高版本进行元编程的一些建议:

    • 首选基于可变参数扩展的算法。与类型列表通常需要的 O(n) 模板实例化链相比,可变参数模板通常由编译器快速处理,并且与旧的 C++98/03 相关。我渴望看到 C++1z 折叠表达式,作为使用包扩展实现 fold 系列元函数的一种方式。

    • 避免继承链:基于递归继承的算法通常表现不佳,因为编译器应该跟踪继承树元数据。更喜欢对下一个 ::type 进行原始调用,而不是直接从下一个“调用”继承作为获取 type 成员的简写。

    • 更喜欢继承包扩展而不是递归继承:也就是说,如果你正在实现一个类似元组的东西,更喜欢

      template<typename... Ts>
      struct tuple : Ts...
      

      template<typename Head, typename... Tail>
      struct tuple : Head, tuple<Tail...>
      

      因为编译器应该在后者上实例化 N 个子类型,加上上面描述的继承开销。

    • 首选基于名称查找的算法:名称查找通常比递归模板实例化执行得更快。因此,请考虑使用 decltype(declval&lt;T&gt;().f()) 之类的方法来计算类型,其中结果是正确的 f 重载。

    【讨论】:

    • 不幸的是,这些都是我已经做过的事情。我发现真正增加编译时间的一些事情是我使用了std::make_sharedstd::shared_ptr。我使用 std::atomic 将这些替换为我自己的引用计数,因为无论如何我都在进行类型擦除。
    • @EmilEriksson 仍然可以使用 std::shared_ptr 而不会产生太多额外开销 blog2.emptycrate.com/content/…
    • 在这种情况下,即使我减少了 std::shared_ptr 实例化的数量,我仍然会有太多实例化,std::shared_ptr 实例化比我自己的实现更昂贵。当然,它之所以昂贵是有原因的,因为标准库类需要灵活性,它将在任何地方使用。但我有一个非常具体的用例,因此,我能够通过使用另一种解决方案来减少编译时间。
    【解决方案2】:

    在我们的项目中,我们只是使用了思考和试验。 IE。首先我们想“这里有什么复杂的模板”,然后我们尝试隔离这些模板或删除它们或重构以查看编译时间是否发生变化。模板编译时间通常伴随着内存使用量的增加,我什至报告过一次 gcc 错误 (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54056)。

    还有一个有用的技巧,使用 -Q 命令行开关(显示 gcc 当前编译的函数)。例如。对于上面链接的错误,很明显 gcc 在那些有错误的模板上放慢了速度。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-23
      • 1970-01-01
      • 2014-04-18
      相关资源
      最近更新 更多