奇怪的JS性能问题

时间:2015-11-12 17:21:14

标签: javascript performance

我的字符串距离功能运行得太慢了。我将其缩小到以下(时间是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/

1 个答案:

答案 0 :(得分:2)

您有3个测试用例:

    带有 3 参数的
  1. Math.min,类型为cast(bool为int)
  2. 2 Math.min使用 2 参数进行调用,其中第二个调用具有类型转换
  3. 2 Math.min使用 2 参数调用,每个参数都没有类型强制转换
  4. 在1和2& 3之间还存在另一个区别,其中一些参数存在额外的+ 1。我认为这是一个错误。

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

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

    这需要250ms(vs 60ms为3)。这仍然表明带有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);
    }
    

    我的计算机上的结果是:1需要2500ms2需要87ms。如果你向Math.min添加更多参数,你会发现每增加一个参数,它会在500ms之间整齐地增加,这可以揭示实现的内容。

    浏览器供应商优化经常使用的功能。如果具有2个以上参数的Math.min不常见,则这些结果并不奇怪。你在这里看到的是Math.min有两个实现:一个用于两个参数,这是非常优化的,另一个用于更多参数,这些参数未经优化。对于v8,这由apsillers确认:v8 source on github