【发布时间】:2020-06-20 22:21:47
【问题描述】:
在使用 Javascript 编写对大型数值数组进行操作的性能敏感代码时(想想线性代数包,对整数或浮点数进行操作),人们总是希望 JIT 尽可能地提供帮助。大致意思是:
- 我们总是希望我们的数组是压缩 SMI(小整数)或压缩双精度数,具体取决于我们是在进行整数计算还是浮点计算。
- 我们总是希望将相同类型的东西传递给函数,这样它们就不会被标记为“超多态”和去优化。例如,我们总是希望在调用
vec.add(x, y)时将x和y都封装为 SMI 数组,或者同时使用封装的 Double 数组。 - 我们希望函数尽可能内联。
当人们偏离这些情况时,会发生突然而剧烈的性能下降。这可能由于各种无害的原因而发生:
- 您可以通过看似无害的操作将压缩的 SMI 数组转换为压缩的 Double 数组,例如
myArray.map(x => -x)的等价物。这实际上是“最好的”坏情况,因为打包的 Double 数组仍然非常快。 - 您可以将打包数组转换为通用盒装数组,例如通过将数组映射到(意外)返回
null或undefined的函数。这种糟糕的情况很容易避免。 - 您可能会通过传入太多类型的东西并将其变成超态来取消优化整个函数,例如
vec.add()。如果您想进行“通用编程”,则可能会发生这种情况,其中vec.add()在您不注意类型的情况下(因此它会看到很多类型进入)以及在您想要勉强的情况下使用达到最佳性能(例如,它应该只接收盒装双打)。
我的问题更像是一个软问题,关于如何根据上述考虑编写高性能 Javascript 代码,同时仍然保持代码的美观和可读性。一些具体的子问题,以便您知道我的目标是什么类型的答案:
- 是否有一套关于如何在 SMI 阵列封装的世界中进行编程的指南(例如)?
- 是否可以在不使用宏系统之类的东西将
vec.add()之类的东西内联到调用站点中的情况下,在 Javascript 中进行通用的高性能编程? - 如何根据宏态调用站点和去优化等问题将高性能代码模块化到库中?例如,如果我愉快地高速使用线性代数包
A,然后我导入了一个依赖于A的包B,但B用其他类型调用它并对其进行去优化,突然(没有我的代码正在更改)我的代码运行速度较慢。 - 是否有任何好的易于使用测量工具来检查 Javascript 引擎在内部对类型所做的工作?
【问题讨论】:
-
这是一个非常有趣的话题,而且是一篇写得很好的帖子,表明你已经正确地完成了你的部分研究。但是我担心问题对于 SO 格式来说太宽泛了,而且它不可避免地会吸引更多的意见而不是事实。代码优化是一个非常复杂的事情,一个引擎的两个版本的行为可能不一样。我认为有一个负责 V8 JIT 的人有时会闲逛,所以也许他们可以为他们的引擎给出一个正确的答案,但即使对他们来说,我认为这对于单个 Q/A 来说主题太宽泛了.
-
“我的问题更像是一个软问题,关于如何编写高性能 Javascript 代码......” 顺便说一句,请注意 javascript 提供了后台进程(网络工作者)的生成,还有一些库可以利用 GPU(tensorflow.js 和 gpu.js)提供手段,而不是仅仅依靠编译来提高基于 javascript 的应用程序的计算吞吐量......
-
@JonTrent 实际上我在帖子中撒了一点谎,我不太关心经典的线性代数应用程序,但更关心整数上的计算机代数。这意味着立即排除了许多现有的数字包,因为(例如)在对矩阵进行行缩减时,它们可能会除以 2,这在我工作的世界中是“不允许的”,因为 (1/2)不是整数。我考虑过网络工作者(特别是对于一些我希望取消的长时间运行的计算),但我在这里要解决的问题是降低延迟,足以响应交互。
-
对于 JavaScript 中的整数运算,您可能正在查看 asm.js 样式的代码,大致是“在每个操作后面加上一个
|0”。它并不漂亮,但你可以用一种没有适当整数的语言来做最好的事情。您也可以使用 BigInts,但截至目前,它们在任何常见引擎中都不是很快(主要是由于需求不足)。
标签: javascript v8 jit spidermonkey