【问题标题】:Compile-time performance of constexpr computationconstexpr 计算的编译时性能
【发布时间】:2019-03-04 19:38:49
【问题描述】:

我有一些非平凡的 C++17 函数标记为 constexpr。他们正在做与图相关的计算(深度优先遍历)和通用算法(例如,查找、排序、唯一...)。

如果我尝试通过将结果放入 constexpr 全局变量中来强制在编译时进行评估,可能会发生 3 件事:

  1. 对于小型计算(给出一个想法,可以说大约 100 个节点的图,节点或多或少是整数)编译很好(大约需要 2 秒)
  2. 对于大约 500 个节点,编译需要大约 1 分钟并占用 30GB 内存 (!)。
  3. 对于大约 1000 个节点,编译需要太多内存,我无法完成。

如果我删除 constexpr 限定符并要求运行时计算,编译和执行会非常快(不到 5 秒)

我使用带有 -O3 -std=c++17 的 g++ 8.2。

为什么要花这么长时间? g++ 是否以 constexpr 的编译时优化问题而闻名? 在编译过程中,constexpr 函数的性能应该如何?据我了解,编译器将自己变成了constexpr 计算的解释器。但我绝对不怀疑,在 Python 中评估相同的程序会非常快,因为数据量非常小。

编辑:提到了这些问题here(GCC 开发人员的博客)

【问题讨论】:

  • 就是这样。如果您对编译器性能不满意,可以退回以获得全额退款。但请记住,正确处理 constexpr 函数是非常非常非常重要的任务。
  • @SergeyA 所以你不会争论 constexpr 计算只适用于琐碎任务的观点吗?看起来就是这样,但并不是我被介绍给 constexpr...
  • @Bérenger constrexpr 评估确实是一项艰巨的任务,编译器必须检查并禁止任何未定义的行为,并且必须输出诊断信息。
  • @GuillaumeRacicot 令我惊讶的是,我观察到的内容与关于 constexpr 的说法之间存在差异。例如,是否有关于人们报告 constexpr 计算有多慢的文档?
  • @Bérenger 我相信有一些记录在案的情况是内存使用(或计算时间)超过预期,但这些是特定于某些编译器版本/报告的问题。我建议您尝试使用不同的编译器进行试验,看看是否有人设法使您的案例更快或更轻松,并为您的实现提供详细的报告。

标签: c++ g++ c++14 constexpr compilation-time


【解决方案1】:

g++ 记忆编译时结构。更重要的是,编译时结构可能会沿两个您想要采用的分支和您不采用的分支创建和检查,除非您小心。

指数爆炸是很有可能的,可能就是你所看到的。

有一些策略可以降低编译时间复杂度。避免深度递归。注意累积符号长度。确保只检查你想取的分支。

我的意思是,检查一个非常简单的:

std::conditional_t< (A<B), make_type_A<Ts...>, make_type_B<Ts...> >

这段代码的作者可能打算只创建一种类型,但这段代码要求创建两种类型。

这不太可能是您的问题,但在运行constexpr 代码时可能会出现类似问题。

对于每次调用,计算出所需状态的大小。将所需的总状态加起来。投入 10 倍的开销。

您还可以通过完成超过 2 个样本来分析问题的 O 表示法。检查 100、200、300、400、500 尺寸图。尝试线性图、平凡图、完全图、具有常数或百分比连通性的随机图。

编译时间增长的 O 表示法可能会帮助您缩小问题所在。如果它是线性的、多项式的或指数的,您将看到不同类型的问题。

急剧变化的线性意味着您遇到了资源瓶颈。也许是记忆。开始绘制其他资源使用情况,看看是否能找到瓶颈。

如果您不记录图表并放大“悬崖”,指数看起来很像带有悬崖的线性。可能有一个狭窄的部分,指数部分将常数因子抛在后面。

多项式变得有趣。多项式的阶数(对数图可以帮助找到它)可以告诉你什么样的操作正在搞砸。就像知道您的传统算法是 O(n^3) 一样,这意味着您正在寻找一个三重循环。 O(n^3) 编译时间意味着您正在以某种方式实例化相当于三重循环。

【讨论】:

    猜你喜欢
    • 2014-01-08
    • 1970-01-01
    • 2018-08-12
    • 2011-08-28
    • 1970-01-01
    • 1970-01-01
    • 2013-08-29
    • 2022-01-19
    • 2013-07-13
    相关资源
    最近更新 更多