【问题标题】:Is JavaScript string comparison just as fast as number comparison?JavaScript 字符串比较和数字比较一样快吗?
【发布时间】:2014-07-13 06:16:55
【问题描述】:

我想为 JavaScript 枚举编写一个小库。为此,我需要决定如何存储枚举值。因此,我想在比较时使用最快的方法,但我也想要一些可调试的东西,所以我在使用字符串或数字之间纠结。我知道我也可以使用对象,但这将是另一个问题

例如

// I don't want this because when debugging, you'd see just the value 0
var Planets = {Earth:0, Mars:1, Venus: 2}

// I'd prefer this so that Planets.Earth gives me a nice readable value ("Earth")
var Planets = {Earth: 'Earth', Mars: 'Mars'}

但我担心当我使用if (myPlanet === Planet.Earth) 比较它们时,字符串比较可能需要更长的时间(比如如果它处于紧密循环中)。应该是这样,因为http://ecma-international.org/ecma-262/5.1/#sec-11.9.6

如果Type(x)是String,那么如果x和y是完全相同的字符序列(长度相同,对应位置的字符相同),则返回true;否则返回false。

但是当我写一个测试用例时,我发现它们花费了相同的时间http://jsperf.com/string-comparison-versus-number-comparison/2,所以它看起来不像是在扫描整个字符串。

我知道这可能是一个微优化,但我的问题是:字符串相等比较是否使用指针完成,因此与数字相等比较一样快?

【问题讨论】:

  • 就像规范说的:如果字符序列相同,则两个不同的字符串是相同的。由于字符串是不可变的,运行时系统可能在它们相同时折叠单独的字符串实例,因此可以在逐个字符比较之前进行直接指针比较。
  • 这正是我的问题,它似乎是这样优化的,我想知道什么时候不是真的?什么时候扫描字符串?
  • 您可能会通过使用 inequality 而不是相等性的 jsperf 测试了解更多信息。
  • 让我也创建它并将其添加到 jsperf
  • 为了保证每个字符串实例都是唯一的,并且所有的字符串都具有相同的值,运行时系统必须在字符串连接发生时进行大量昂贵的查找引用指向同一个实例。我怀疑它会这样做。当然,解析器可能会为任何单个代码块中的常量执行此操作。

标签: javascript string performance enums


【解决方案1】:

字符串比较可以“一样快”(取决于实现和值) - 或者它可以“慢得多”。

ECMAScript specification 描述的是语义,而不是实现。确定知道的唯一方法是创建一个适用性能基准,并在特定实现上运行它。

简单地说,我希望是这种情况1字符串实习特定实现的影响正在观察中。 p>

也就是说,所有来自字面量的字符串值(不是字符串对象)都可以简单地放入一个池中,使得implIdentityEq("foo", "foo") 为真——也就是说,只需要一个字符串对象。这种实习可以在不断折叠之后完成,这样"f" + "oo" -> "foo" - 再次,只要它支持 ECMAScript 语义,每个特定的实现。

如果完成了这样的实习,那么对于implStringEq,第一个检查可能是评估implIdentityEq(x,y),如果为真,则比较是平凡的,并且在 O(1) 中执行。如果为 false,则需要进行正常的字符串逐字符比较,即 O(min(n,m))。

(也可以使用x.length != y.length 来确定直接错误,但在这里似乎不太相关。)


1 虽然在上面我认为 for 字符串实习是一个可能的原因,但现代 JavaScript 实现执行 很多 优化 - 因此,实习只是可以(并且已经)完成的各种优化和代码提升的一小部分!

我创建了一个 "intern breaker" jsperf。这些数字与上述假设一致。

  1. 如果一个字符串被实习,那么比较在性能上近似于测试“身份” - 虽然它比数字比较慢,但这仍然比逐字符快得多-字符串比较。

  2. 根据上述断言,IE10 似乎并未考虑对象身份来进行快速通过字符串比较,尽管它确实使用了快速失败长度检查。

  3. 在 Chrome 和 Firefox 中,两个不相等的实习字符串的比较速度也与两个不相等的字符串一样快 - 比较两个 不同 的实习字符串可能存在特殊情况.

  4. 即使对于小字符串(长度 = 8),实习也可以更快。 IE10 再次表明它没有这种“优化”,即使它似乎有一个有效的字符串比较实现。

  5. 当遇到第一个不同的字符时,字符串比较可能会很快失败:即使比较等长的长字符串也可能只比较前几个字符。


  • Do common JavaScript implementations use string interning?(但没有给出参考)

    是的。通常,JS 源代码中的任何文字字符串、标识符或其他常量字符串都是实习的。然而,实现细节(例如,究竟是什么)会有所不同,以及何时发生的实习

  • 参见JS_InternString(FF 确实有字符串插入,虽然我不知道字符串在哪里/如何从 JavaScript 中隐式插入)

【讨论】:

  • 我确实创建了测试 :) 从我的测试来看,Firefox 实习生似乎甚至动态生成字符串,至少在某些情况下,Chrome 没有。此外,无论是否为文字,IE 都是最慢的刺痛比较。
  • @JuanMendes 在多次失败后,我终于修复了my jsperf,以便它可以展示这种情况。
【解决方案2】:

一般来说,最好的字符串实习(将具有给定值的字符串变成唯一引用或 O(1) 可比较符号)将花费 O(n) 时间,因为如果没有它就无法有效地做到这一点看着所有涉及的角色。

因此,相对效率的问题相当于实习期将摊销多少次比较。

在极限情况下,一个非常聪明的优化器可以提取静态表达式,这些表达式构建字符串并实习一次。

上面的一些测试使用了将被实习的字符串,在这种情况下,比较可能是 O(1)。如果枚举基于到整数的映射,则在任何实现中都是 O(1)。

当至少有一个操作数是真正动态的字符串时,就会出现昂贵的比较情况。在这种情况下,不可能在小于 O(n) 的时间内比较相等性。

应用于最初的问题,如果希望用不同的语言创建类似于enum 的东西,唯一的注意是确保实习可以只在几个地方完成。如上所述,不同的浏览器使用不同的实现,这可能会很棘手,并且正如IE10 中指出的那样,可能是不可能的。

警告缺少字符串实习(在这种情况下,您需要枚举实现的整数版本),如果@JuanMendes 安排myPlanet 变量的值,则基于字符串的枚举实现基本上是 O(1)在 O(1) 时间内设置。如果使用Planets.value 设置,其中 value 是已建立的行星,它将是 O(1)。

【讨论】:

    【解决方案3】:

    有些情况下字符串比较会慢很多(比较动态生成的字符串)

    以下测试比所有其他测试慢 77%(在 chrome 和 IE 中)

    var StringEarth = 'Ear' + 'th';
    for (var i = 0; i < ITERATIONS; i++) {
      x = StringPlanets.Venus === StringEarth;
    }
    

    问题中提到的测试中的缺陷是我们正在针对文字字符串进行测试。似乎 JavaScript 已经过优化,因此字符串文字的字符串比较仅通过测试指针来完成。这可以通过动态创建字符串来观察。我最好的猜测是文字字符串池中的字符串被标记,以便它们可以仅使用地址进行比较。

    请注意,即使对于动态字符串,字符串比较在 FF 中似乎也一样快。此外,即使是文字字符串也一样慢。

    结论所有浏览器的行为都不同,因此字符串比较可能会或可能不会更慢。

    【讨论】:

      猜你喜欢
      • 2013-05-06
      • 2013-02-12
      • 2011-06-21
      • 2012-02-09
      • 1970-01-01
      • 1970-01-01
      • 2012-06-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多