我想为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,因此它似乎不会扫描整个字符串。
我知道这可能是一个微优化,但我的问题是:是否使用指针进行字符串相等比较,因此与数字相等比较一样快?
答案 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 。这些数字与上述假设一致。
如果一个字符串被中断,那么比较是性能的近似值来测试" identity" - 虽然它比数字比较慢,但速度要快得多而不是逐个字符的字符串比较。
持有上述断言,虽然IE10确实使用了快速失败长度检查,但IE10似乎没有考虑传递快速字符串比较的对象标识。
在Chrome和Firefox中,两个不相等的实习生字符串也会快速比较两个 - 很可能有一个特殊情况可以比较两个不同的实习词。
即使是小字符串(长度= 8),实习也会快得多。 IE10再次表明它没有这个"优化"即使它似乎有一个有效的字符串比较实现。
字符串比较可能会因为很快而失败,因为遇到第一个不同的字符:即使比较相等长度的长字符串也只能比较前几个字符。
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)。