JavaScript中位运算符的性能表现

21

在像C++/Java/C#等语言中使用位运算符的主要理念之一是它们非常快速。但我听说在JavaScript中,它们很慢(尽管几毫秒可能今天无关紧要)。为什么会这样?

这个问题讨论了何时使用位运算符,因此我将此问题的重点转移到性能方面。)


位运算在现代编译器开发中并不是“非常快”,如果你的意思是 i<<1 比 i*2 快任何一点。 - Jimmy
1
@Jimmy:现代编译器有多先进并不重要,30年前的编译器就已经聪明到知道i<<1比i*2稍微快一点了...你可能是想表达“在现代CPU速度的这个阶段”,因为在今天的CPU上,纳秒级别的差异几乎可以忽略不计。 - Kip
12个回答

20

这是一个相当古老的问题,但似乎没有人回答更新版本。

JavaScript中存在的性能损失,在C/C++中是不存在的。这是由于将浮点数表示法(JavaScript存储所有数字的方式)转换为32位整数进行位操作及还原。


6
这不再真实,考虑到大多数JS JIT对于保持数字只是int类型如果没有需要浮点操作的情况下都非常好。虽然规范要求转换,但只要最终结果相同,实际实现不必按照规范操作。 - Joey
4
有道理,然而这仍适用于所提出的问题。 - Chad Schouggins

15

现在没人再使用十六进制了吗?

function hextoRgb(c) {
    c = '0x' + c.substring(1);
    return [(c >> 16) & 255, (c >> 8) & 255, c & 255]; 
}

var c1 = hextoRgb('#191970');
alert('rgb(' + c1.join(',') + ')');

3
实际上,将字符串转换的方式对我来说有点奇怪。但另一方面,它比如下的代码更容易阅读:return /#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i.exec(c).slice(1).map(function(e) {return parseInt(e, 16);}); - Robert
1
@Robert - 这看起来可能有些奇怪,但它比你建议的函数更快,因为它使用了正则表达式、循环和闭包。 - newshorts

10

我在JS中使用零位移操作进行快速整数截断:

var i=3.141532;
var iTrunc=i>>0; //3

26
那是位移运算符的一种极其可憎的用法。 - Greg Hewgill
5
是的,我知道它很糟糕,但实际上,在Flash游戏编程中,它有其存在的意义。在AVM1时代,这是最快的方法。不确定JS是否也适用。 - spender
10
"var iTrunc = i|0;" 这行代码可以帮你省略一次输入 "Math.trunc()" 的操作(我在某个 Code Golf 的话题里看到过这种写法)。 - Jimmy
20
编程是确保程序员和计算机理解你所做的事情,如果满足这些条件,惯用语是完全适当的。所有语言都有惯用语,甚至包括英语。惯用语可能一开始不太熟悉,但只要它们是(1)易记住的和(2)可识别的,在程序员的词汇表中使用它们是非常合适的。 - Plynx
2
我认为你需要意识到这是游戏编程,而这种事情既普遍又必要。然而,拥有解释项目性能策略的良好文档是一个好主意。你应该看看在 JVM 中最小化垃圾收集所需的一些技巧。 - martyman
显示剩余3条评论

9

何时需要使用它们?当您想执行位运算时,您会想要使用它们。就像您使用布尔运算符执行布尔运算一样,使用数学运算符执行数学运算一样。

如果您熟悉位运算符,则在某些应用程序中使用它们非常自然。它们可以用于许多其他目的,而不仅仅是超优化的布尔数组。当然,在JavaScript编程中很少遇到这些情况,但这并不是运算符不应该可用的原因。


5
我在这里找到了一些关于位运算的好资料。显然,现在它们表现得非常出色。你为什么要使用它们?和其他地方一样,同样的原因。

1
我晚了3年才来参加派对,但感谢你分享这篇好文章 =) - aschyiel
@aschyiel 嗯,我迟到了十年.. 派对还在吗? - J.G.Sebring

3

我认为操作符的效率高低取决于实现者。例如,没有任何东西可以阻止JavaScript实现者制作一个即时编译虚拟机,将位运算转化为一条机器指令。因此,“JavaScript中的位运算符”本身并不慢。


2
@Snarfblam 这种改进 JavaScript 实现的“理论”在 JavaScript 短暂的历史中有许多实际先例。此外,我不确定 e.e.coli 的观点是否也是如此,所有 CPU 都支持快速位运算,因此在这个低级别上,位运算本质上并不慢,所以如果由于某些原因 JavaScript 实现未能将 js 位运算松散地映射到这些高效的机器操作,这可以轻松解决,并且如果有任何迹象表明这个问题对大多数 js 使用来说实际上是瓶颈,那么解决这个问题会更快。 - mjv

3

有一个用JavaScript编写的NES模拟器 - 它似乎充分利用了按位操作。


2
我怀疑在JavaScript中,位运算并不特别慢。由于这些操作可以直接映射到CPU操作,而CPU操作本身相当高效,因此似乎没有任何固有的特征会使位运算在JavaScript中无法修复地变慢。
2015年12月编辑: 我错了!JavaScript在位运算方面的性能损失来自于需要从浮点数转换为整数,然后再转回去(因为JavaScript中的所有数字变量都存储为浮点值)。感谢Chad Schouggins指出这一点。

尽管如此,正如几个回答中所指出的,存在各种应用程序使用JavaScript依赖于位运算(例如密码学和图形),而这些应用程序并不特别慢...(请参见此页面上的silky和Snarfblam)。这表明,虽然比C/C++和其他将位运算直接转换为单个本机CPU指令的语言要慢,但位运算并不是那么缓慢。

让我们仍然考虑某些特定原因导致JavaScript主机的各种实现者以使这些操作极其缓慢的方式实现位运算,并看看这是否重要...

尽管JavaScript已用于其他目的,但此语言最常见的用途是提供用户界面类型的服务
顺便说一句,我绝不是以任何贬义的方式表达这个意思;执行这些智能UI功能,并考虑到对语言施加的各种限制以及对标准的松散遵守,需要-并且仍然需要-有才华的JavaScript黑客。
关键是,在UI类型需求的背景下,需要进行任何可能暴露JavaScript处理此类操作的缓慢性的大量位运算的需求最多也只是偶尔发生。因此,对于典型的用途,程序员应该在整体程序/数据流与位运算方法相符的情况下使用位运算,并且他们应该很少关注性能问题。在位运算使用引起性能瓶颈的不太可能的情况下,可以重新设计事物,但最好避免过早优化。

上述规则的一个显著例外是引入画布。在现代浏览器上,我们可以预期JavaScript主机将需要更多原始图形函数,而这些操作在某些情况下可能需要大量的位运算(以及大量的数学函数)。这些服务很可能最终会通过JavaScript库的方式得到支持(甚至成为语言附加组件)。对于这样的库,行业智慧将被利用来找出最有效的方法。此外,如果确实存在JavaScript性能在位运算方面的弱点,我们将得到一些帮助,因为我预测各种主机(浏览器)上的JavaScript实现将被修改以改进此特定领域。(这将遵循多年来我们所看到的JavaScript演变的典型模式。)


1

在Windows脚本宿主JScript中使用JavaScript,您可能需要使用位运算符从WMI或Active Directory调用返回的值中选择标志。例如,AD中用户记录的用户访问值包含打包到一个长整数中的多个标志。

ADS_UF_ACCOUNTDISABLE = 0x00000002;

if (uac & ADS_UF_ACCOUNTDISABLE == ADS_UF_ACCOUNTDISABLE) {
  // user account has been disabled
}

或者某人的任意表结构可能包含这样一个字段,可以通过JScript中的ADO访问。

或者您可能希望将检索到的一些数据转换为任何平台上的二进制表示,仅仅因为:

BinaryData = "L";
BinaryString = BinToStr(BinaryData, ".", "x");

// BinaryString => '.x..xx..'

因此,有许多原因为什么人们想在JavaScript中进行位操作。就性能而言,唯一的方法是编写并测试它。我怀疑在大多数情况下,它将是完全可接受的,不会比这些系统包含的其他众多低效率方式更糟糕。


1

当速度至关重要时,您可以使用它们进行位掩码:http://snook.ca/archives/javascript/storing_values/

此外,如果您需要支持Netscape 4,您将使用它们来处理Document.captureEvents()。并不是任何值得尊敬的公司都会让您为NS4编写JS...


2
欢迎来到地狱。你有两个选择来度过永恒的痛苦:在炭烤架上烧烤,或者为NS4编写JS代码。 - Stefano Borini

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接