I've seen some performance critical javascript code, like the one on this project that makes extensive use of bitwise OR operations with 0. Ex:
GameBoyAdvanceCPU.prototype.write8 = function (address, data) {
address = address | 0;
data = data | 0;
this.memory.memoryWrite8(address | 0, data | 0);
I know about the use case of flooring numbers with "|0", but that isn't the case here, as these are always int's. It looks a bit like asm.js, is this to tell the js engine that we are working with integers, allowing some optimizations? If so, which browsers will make those optimizations?
Any pointers to how this works would be appretiated.
According to JavaScript Performance for Madmen
Wrapping integer arithmetic expressions in
( ) | 0
allows the runtime to be sure that you're doing integer arithmetic instead of floating-point arithmetic. This allows it to avoid checking for overflow and produce faster code in many cases.
and according to the page, it's true for "most" Javascript runtimes, but doesn't say which.
As a second source, Writing Fast JavaScript For Games & Interactive Applications states
To tell JavaScript engine we want to store integer values [...] we could use bitwise or operator:
and a third source from Microsoft's Writing efficient JavaScript page:
[...] explicitly tell the JavaScript runtime to use integer arithmetic [...] use the bitwise or operator
Also, apart from in comments, none of the pages above mention asm.js, so I suspect such optimizations apply in code not explicitly marked as asm/in browsers that don't explicitly recognize it.
Referencing the Ecmascript 5 spec: 11.10 Binary Bitwise Operators, namely
The production
A : A @ B
, where@
is one of the bitwise operators in the productions above (&
;^
;|
), is evaluated as follows:Let
lref
be the result of evaluating A.
Letlval
beGetValue(lref)
.
Letrref
be the result of evaluating B.
Letrval
beGetValue(rref)
.
Letlnum
beToInt32(lval)
.
Letrnum
beToInt32(rval)
.
Return the result of applying the bitwise operator@ tolnum
andrnum
. The result is a signed 32 bit integer.
And noting that ToInt32()
is defined as
Let
number
be the result of callingToNumber
on the input argument.
If number isNaN
,+0
,−0
,+∞
, or−∞
, return+0
.
LetposInt
besign(number) * floor(abs(number))
.
Letint32bit
beposInt
modulo2^32
; that is, a finite integer valuek
ofNumber
type with positive sign and less than2^32
in magnitude such that the mathematical difference ofposInt
andk
is mathematically an integer multiple of2^32
.
Ifint32bit
is greater than or equal to2^31
, returnint32bit − 2^32
, otherwise returnint32bit
.
It then logically follows (which you can confirm in your own console) that for example
((Math.pow(2, 32)) + 2) | 0 === 2
(Math.pow(2, 31)) | 0 === -2147483648 === -(Math.pow(2, 31))
And so forth.
Shortly put, the operation turns the number to a 32-bit integer (which has its knacks, see the second example above and the ToInt32()
definition for an explanation) and then does a logical or with zero which doesn't change the output beyond the first conversion.
Essentially it's a very cost-efficient way to turn a number into a 32-bit integer because 1) it relies on browser's built-in ToInt32()
; and 2) ToInt32(0)
short-circuits to 0
(see the spec above) and therefore adds practically no additional overhead.
What it actually does can be seen in this fiddle
It's probing the variable against integer type in this case and either "flooring" or set it to 0 if not an integer.
Thus, there's a tremendous differnece to a = a || 0
which would leave a value of 3.2
untouched.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With