如何优化APL以在阵列处理中具有出色的性能?它执行一些示例技巧和优化?

Dan*_*mon 5 performance interpreted-language apl compiler-optimization

我对APL的工作效率如此之高,甚至有时被基准为优于C的性能感兴趣。因此,我很好奇,APL编译器为使语言如此高效而进行了哪些优化?

Adá*_*dám 5

以下是有关如何完成的一些示例:

相关地,这些讨论了上述背后的原则:

  • 根据运行时看到的数据模式以及惰性/thunked APL 如何完全跳过某些代码来选择算法:视频

  • 通过读取整个简单数组并通过无分支代码避免分支预测失败来减少寻找延迟:视频(APL 与这些想法保持一致,并鼓励这些风格,比许多其他语言更容易)


小智 5

您无法在性能方面比较两种语言(例如 C 与 APL),因为性能在很大程度上取决于语言的实现和所使用的库。同一个 C 程序在一个平台上可能很慢(例如:Windows),但在另一个平台上可能很快。关键点是性能几乎完全是语言给定实现的属性,而不是语言本身的属性。

\n\n

就 APL 而言,可以将给定操作所需的 CPU 周期分为两部分:解释器开销(构成 APL 程序的标记的处理)和原语(例如加法、归约等)。在大多数 APL 解释器中,解释器开销相当小(这意味着该部分的优化无法获得太多性能(又名阿姆达尔定律)。在早期 APL(例如 1970 年)中,情况有所不同。当前解释器中 APL 原语的处理是在 C/C++ 中实现,因此部分 CPU 周期在性能方面与 C 相同(再次记住,实现可能会有所不同)。

\n\n

我在 APL 原语级别(从简单(整数加法)到不那么简单(复杂余弦余弦)的不同标量函数以及它们的外积)执行了一些基准测试。有点令人惊讶的结果是不同标量函数的性能不是由计算函数的复杂性决定,而是由内存(包括缓存)的访问时间和 CPU 的分支预测决定。例如,如果您在循环中执行相同的 APL 操作,则第二次迭代将通常速度是第一次的两倍,随后的迭代将在大约第四次迭代后稳定下来(在 i5-4570 CPU 上)。

\n\n

测量值波动很大,这使得老式的性能测量(例如解释器 X 的速度是解释器 Y 的两倍)变得毫无意义。

\n\n

根据经验,如果 APL 程序的平均向量大小(即 \xe2\x8d\xb4,X)为 20 或更大,那么您可以完全忽略解释器开销,并且 APL 程序的性能与类似的 C 程序。

\n\n

APL 比 C 更快的情况(这在理论上是不可能的)通常可以追溯到 C 和 APL 中使用了不同的算法。现实生活中一个典型的例子是在一种情况下使用堆排序,在另一种情况下使用快速排序。这又是实现上的差异,而不是语言本身的差异。

\n

  • *与 **可比** C 程序相同的性能* - 但这正是重点。对于 C 程序员来说,要获得相同的性能,他们基本上必须经历 APL 解释器数十年来的进化微调。这就是解释性语言的优势,解释器的改进有利于所有现有代码。这很像 C 编译器的改进有利于所有重新编译,但解释增加了一个额外的有益层。如果没有这个,在 C 中获得与 APL 相同的性能是不现实的。 (2认同)
  • *在一种情况下使用堆排序,在另一种情况下使用快速排序* - 不,C 程序员可以选择一种算法或另一种算法,但 APL 解释器可以实现所有排序算法,然后即时选择它需要的算法根据数据的外观,该特定排序实例的估计速度将是最快的。大多数 C 程序员不会在每次只需要排序的时候都花这么大的力气,因为这会花费太多的精力。在解释型语言中,每种方法只需实现一次,并结合一些启发式方法来决定使用哪种方法。 (2认同)