0

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

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

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

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

我将 g++ 8.2 与 -O3 -std=c++17 一起使用。

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

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

4

1 回答 1

2

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) 编译时间意味着您正在以某种方式实例化相当于三重循环的内容。

于 2019-03-04T21:00:40.920 回答