JavaScript字符串比较和数字比较一样快吗?

时间:2014-05-23 19:10:36

标签: javascript string performance enums

我想为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,因此它似乎不会扫描整个字符串。

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

3 个答案:

答案 0 :(得分:13)

字符串比较可能"同样快#34; (取决于实施和价值观) - 或者可能更慢"。

ECMAScript specification描述了语义,而不是实现。了解某些问题的唯一方法是在特定实现上运行适用性能基准。

平凡,我希望情况 1 ,正在观察字符串实习特定实现的影响。

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

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

(立即虚假也可以用x.length != y.length确定,但这似乎不太重要。)


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

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

  1. 如果一个字符串被中断,那么比较是性能的近似值来测试" identity" - 虽然它比数字比较慢,但速度要快得多而不是逐个字符的字符串比较。

  2. 持有上述断言,虽然IE10确实使用了快速失败长度检查,但IE10似乎没有考虑传递快速字符串比较的对象标识。

  3. 在Chrome和Firefox中,两个不相等的实习生字符串也会快速比较两个 - 很可能有一个特殊情况可以比较两个不同的实习词。

  4. 即使是小字符串(长度= 8),实习也会快得多。 IE10再次表明它没有这个"优化"即使它似乎有一个有效的字符串比较实现。

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


    • Do common JavaScript implementations use string interning?(但未提供参考资料)

        

      是。通常,JS源中的任何文字字符串,标识符或其他常量字符串都是实体。但是,实施细节(例如,实际上是实施的内容)会有所不同,以及发生实习时

    • 请参阅JS_InternString(FF确实有字符串实习,但字符串在哪里/如何与JavaScript隐式交互,我不知道)

答案 1 :(得分:1)

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

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

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

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

请注意,即使对于动态字符串,字符串比较在FF中也同样快。而且,即使是文字字符串也一样慢。

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

答案 2 :(得分:1)

通常,最好的字符串实习(将具有给定值的字符串转换为唯一引用或O(1)可比较符号)将花费O(n)时间,因为它不能这样做有效地无需查看所涉及的所有角色。

相对效率的问题相当于实际分摊的实际比较数量。

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

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

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

如果适用于原始问题,如果希望用不同语言创建类似于enum的东西,那么唯一的了望就是确保实习只能在少数几个地方完成。如上所述,不同的浏览器使用不同的实现,因此可能很棘手,并且如IE10中指出的那样可能是不可能的。

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