奇怪的JS性能问题

Strange JS performance issues

本文关键字:性能 问题 JS      更新时间:2023-09-26

我的字符串距离函数工作速度太慢。我已经将其缩小到以下内容(时间用于10K字符串比较):

// desired behavior - 400ms
dp[c + 257] = Math.min(dp[c + 256] + 1, dp[c + 1] + 1, dp[c] + (a[i] != b[j]));
// this is somewhat faster - 300ms
dp[c + 257] = Math.min(dp[c + 256] + 1, dp[c + 1] + 1);
dp[c + 257] = Math.min(dp[c + 257], dp[c] + (a[i] != b[j]));
// even faster - 50ms
dp[c + 257] = Math.min(dp[c + 256] + 1, dp[c + 1] + 1);
dp[c + 257] = Math.min(dp[c + 257], dp[c] + (a[i] != b[j] ? 1 : 0));

因此,首先,将Math.min拆分为两个调用比使用一次3个参数更快——这怎么可能呢?第二,为什么添加显式的第三表达式比依赖于从bool到int的隐式转换要快得多?

这是一把小提琴:https://jsfiddle.net/6bnLvbt6/

您有3个测试用例:

  1. 带有类型强制转换(bool到int)的3参数的Math.min
  2. 2Math.min调用,每个调用带有2个参数,其中第二个调用具有类型强制转换
  3. 2Math.min调用,每个调用带有2个参数,但不带类型强制转换

在1和2&3,其中在一些自变量上存在额外的CCD_ 5。我认为这是一个bug。

隐式类型转换似乎是最昂贵的。如果从1中删除隐式类型转换,则会得到:

dp[c + 257] = Math.min(dp[c + 1], dp[c + 256], dp[c] + (a[i] != b[j] ? 1 : 0));

这需要250ms(相对于3的60ms)。这仍然表明具有3个参数的Math.min较慢。让我们调查更多。

如果您将测试用例简化为:

// 1
for(var i = 0; i < 10000; i += 1) {
    Math.min(1, 2, 3);
}
// 2
for(var i = 0; i < 10000; i += 1) {
    Math.min(1, 2);
    Math.min(1, 3);
}

在我的机器上的结果是:12500ms287ms。如果您在Math.min中添加更多的参数,您会发现每增加一个参数,它就会在时间上整齐地增加500ms,这就揭示了实现的一些内容。

浏览器供应商优化经常使用的功能。如果具有两个以上自变量的Math.min是不常见的,则这些结果并不奇怪。您在这里看到的是,Math.min有两个实现:一个用于两个参数,它确实得到了很好的优化,另一个用于更多参数,它没有得到优化。对于v8,这一点得到了apsillers:github 上的v8源

的证实