【问题标题】:generic way to evaluate a function cost at compile time在编译时评估函数成本的通用方法
【发布时间】:2020-06-17 04:48:59
【问题描述】:

我目前正在研究多维数组迭代器的实现。考虑到两个连续范围(用于 std::equal、std::copy 目的)上的迭代,这些范围表示具有不同对齐方式的兼容数据(二维中的行与列主要),我想找到每个迭代器的步幅顺序给出最快执行时间。

例如:

row of vector components = A -> m elements
row of vectors =           B -> n elements
2D plan of vectors =       C -> 3 elements
row of plan of vectors =   D -> 10 elements

given the datas ordered by ascending strides:
first array:   B | A | C | D
second array:  B | A | D | C

 Obviously, we can iterate over both iterators by bunches of m*n elements. Then:

 If we choose the first array convention, the first iterator is contiguous and the second one 
 will perform (3 - 1)*(10 - 1) jumps forward with a stride of 10 and (10 - 1) jumps backward.

 If we choose the second array convention, the second iterator is contiguous and the first one will 
 perform (10 - 1)*(3 - 1) jumps forward with a stride of 3 and (3 - 1) jumps backward.

=> The second convention is better at everything in this example.

由于我必须考虑很多因素,例如内存来回、连续性和迭代器实现本身(这不是微不足道的),我想执行一个实验计划。但我也知道编译时的一切(大小和步幅),因此在编译时为每个模板实例化执行实验计划会很酷。我的问题是:

是否可以在编译时评估某些指令的运行时成本,在编译时除了输入数组的内存地址之外的所有内容都是已知的?

【问题讨论】:

  • 你能提供示例代码吗?
  • 我不能,因为实施将取决于答案。基本上,我只想说: std::copy(a.beginWith(), a.endWith(), b.beginWith())
  • 只要指令运行时成本足够可重现,它是可能的。可靠的方法是在运行时对其进行评估,推导出一个数学定律(可能是一个愚蠢的查找表),然后在编译时使用该定律。
  • @Oliv:我可以认真对待你的提议,谢谢!如果您以可能的系统方法发布答案,我会给您我的赏金。找到适合外部测试程序输出的宏替换的预构建步骤会更好。
  • 其实我是一名物理学家,我向你推荐的方法只是我工作的核心:en.wikipedia.org/wiki/Scientific_method。我从来没有用它来专门研究计算机作为物理对象。但我认为这是一种相关的方法,因为计算机行为对于理论家来说太复杂了。

标签: c++ arrays optimization iterator constexpr


【解决方案1】:

没有。您的问题基于错误的假设。

一些不好的假设(可能还有其他的):

  • 函数按原样使用:编译器可能会在许多地方内联它,或者决定最好将它放在一个单独的函数中,因为代码大小会提高。由于您可以通过周围的代码获得稍微不同的行为,因此您可能会看到不同的性能。
  • 指令是有代价的:处理器在许多情况下会乱序运行指令,或者它们会并行化指令。如果被其他内存访问包围并摊销成本,则可能会隐藏像除法这样可能需要很长时间的东西。
  • 性能独立于处理器。编译器不知道您将在哪个特定处理器上运行、缓存或缓存线有多大、主存储器有多快或分支预测的好坏程度。所有这些都会对性能产生巨大影响。

您可以做的是分析和衡量。使用此功能分析应用程序,看看您是否真的需要修复它。衡量您获得的性能并尝试不同的选项。

【讨论】:

  • 感谢您的回答。但是,知道编译器能够在运行时之前决定要做什么,它应该能够在多个版本之间做出决定,在等效情况下随机选择一个,即使在必要时每次调用也是如此。
  • 我只是说,既然 constexpr 指令得到了很好的支持,那么除了衡量一切的通常共识之外,还有一些事情要做。实际上,我要实现一个带有计数器的迭代器,遵循所涉及的 for 循环,索引计数......模拟 gcc 自然地做的一个痛苦的尝试。也许有人可以找到涉及两个构造函数的技巧,并让编译器省略较重的内容?我不知道,这是黑魔法:)
  • @Cevik 拿一个比较线性搜索和二分搜索的基准。线性搜索在短集上更快。二分搜索变得更快的确切点取决于系统(缓存、速度、内存等)。基准是一个二进制程序,已经编译,如果你愿意的话可以冻结。即使生成的代码没有变化,您在不同的系统上仍然会得到不同的结果。您要求的是编译器为所有情况选择正确的选项,这在一般情况下是不可能的。
  • 我理解你关于临界点的观点,但是像循环展开这样的简单示例表明编译器能够决定遵循 -O3 选项等等。即使编译器并非每次都正确,这也是我们大部分时间不会优化的默认行为。
  • 其实这个问题让我想起了不完备性定理和停机问题,这暗示着我可能没有通用的方法来评估编译时的函数成本。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-02-13
  • 2015-03-21
  • 2012-12-24
  • 1970-01-01
  • 2011-09-27
  • 2015-04-23
  • 1970-01-01
相关资源
最近更新 更多