13

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.

Seki
  • 11,135
  • 7
  • 46
  • 70
Diogo Franco
  • 486
  • 2
  • 4
  • 13
  • @RobG it's not equivalent for the `4.2` value. – zerkms May 10 '15 at 20:43
  • 2
    This is asm.js style. It coerces the value to a 32-bit integer, which is faster than floating point. – Raymond Chen May 10 '15 at 20:49
  • @RobG: And not for `3141592658` either. – Bergi May 10 '15 at 20:52
  • @RaymondChen: however, it is not exactly asmjs, as there is no `"use asm";` directive in the code and it uses prototype methods as well. – Bergi May 10 '15 at 20:54
  • 1
    There's a [single commented-out instance of `"use asm"`](https://github.com/taisel/IodineGBA/blob/a7a137c6ffe4f58e789143f311d2108458468d59/IodineGBA/IodineGBA/CPU/ARMCPSRAttributeTable.js#L19) in the repository, suggesting that this is indeed supposed to be asm.js, or at least some of it once was. – user2357112 May 10 '15 at 20:55
  • @Bergi That's why I said asm.js *style*. – Raymond Chen May 10 '15 at 20:56
  • 1
    Looking for another [hint]— "I'll go ahead and rewrite some io register crap that used anonymous function arrays as a workaround this into switches." :) -(https://github.com/chrisdickinson/IodineGBA/commit/44b84f59ae8f95557b2afba4154396c193d54870 ) – l'L'l May 10 '15 at 20:58
  • 2
    From the issue tracker, it looks like the focus is on optimisation: "Honestly firefox & chrome aren't as fast as they should be when running this ... I place type enforcement guards in the code, so browsers should be picking them up more often." - https://github.com/taisel/IodineGBA/issues/21 – fgb May 10 '15 at 21:13
  • 1
    It factors out types, even if it's not asm.js. If you follow the logic, the variables passed into the function, and then passed into the function call inside are forced to int. This logic should hold on modern browsers that don't do asm.js but are getting smarter about type following. – Grant Galitz May 18 '15 at 12:56
  • 1
    I wrote the code snippet in question, the bitwise OR against 0 is a NO-OP. Its use is to provide static typing (for performance reasons) by forcing integer coercion on variables. The project was tested against Firefox. – Grant Galitz May 18 '15 at 14:48

4 Answers4

6

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.

Michal Charemza
  • 25,940
  • 14
  • 98
  • 165
  • 1
    I've come across the claim a number of times, but I'm yet to see anyone actually demonstrate direct speed gains anywhere apart from fast conversion. – Etheryte May 11 '15 at 08:00
3

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.
Let lval be GetValue(lref).
Let rref be the result of evaluating B.
Let rval be GetValue(rref).
Let lnum be ToInt32(lval).
Let rnum be ToInt32(rval).
Return the result of applying the bitwise operator@ to lnum and rnum. The result is a signed 32 bit integer.

And noting that ToInt32() is defined as

Let number be the result of calling ToNumber on the input argument.
If number is NaN, +0, −0, +∞, or −∞, return +0.
Let posInt be sign(number) * floor(abs(number)).
Let int32bit be posInt modulo 2^32; that is, a finite integer value k of Number type with positive sign and less than 2^32 in magnitude such that the mathematical difference of posInt and k is mathematically an integer multiple of 2^32.
If int32bit is greater than or equal to 2^31, return int32bit − 2^32, otherwise return int32bit.

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.

Etheryte
  • 24,589
  • 11
  • 71
  • 116
  • 1
    +1, very nice. I'm still interested in the optimization angle of this though: Why would one want to make the browser call toInt32, is it that the js engines then perform some operations faster? – Diogo Franco May 10 '15 at 22:31
  • 1
    @DiogoFranco As I commented on another answer, I've seen people _claim_ it offers performance gains a number of times, but I'm yet to see anyone demonstrate code where it actually does result in better performance, aside from using it as a conversion to ensure parameters are of a given type. Tests like [this Jsperf I created just now](http://jsperf.com/bitwise-or-0-performance) don't show any recognizable speed differences (I tried a number of different arithmetic and logical operations with different parameters, feel free to experiment yourself). – Etheryte May 11 '15 at 08:03
  • Benefits in simple jsperf benches will not show results for this kind of thing. It involves providing static typing hints to the browser and easing out on bailout type guards when the browser JITs this stuff when running long-term. – Grant Galitz May 18 '15 at 13:00
  • @GrantGalitz I'm still waiting for anyone to show a demo of an actual measurable performance gain. – Etheryte May 18 '15 at 13:26
  • Aye, well I rewrote IodineGBA's parts a few times, and it started out lacking these spurious " |0"s. I tested adding and removing them a few times, as I tweaked it personally against Firefox. Would be nice to boil down the performance difference more with a test, but I'm still under the assumption larger projects experience different compilation tradeoffs versus toy benches. – Grant Galitz May 18 '15 at 14:44
  • Is this toInt32 function exposed somewhere? bc it would be far better way I guess? – Minsky Jun 05 '23 at 12:17
1

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.

Axel Amthor
  • 10,980
  • 1
  • 25
  • 44
-2

| operator is bitwise OR. It's used to do a bit by bit OR operation on two integers.

The usage here is a shortcut very similar to logical OR || operator to provide default value, with the exception that the result is integer only (as opposed to string...etc)

address = address | 0;

means "if address is a number, let's use it; otherwise, set it to 0".

BernieDADA
  • 306
  • 3
  • 10
  • What is the point to do so? Why to prefer it to `||` (which would actually short-circuit if truthy)? – zerkms May 10 '15 at 20:46
  • @zerkms, I'm not sure. I can only guess maybe they want to make sure it's a number so it doesn't break the code. If address is "foo", the || won't take effect... – BernieDADA May 11 '15 at 03:11
  • If an operation is on the leftside of `| 0`, the result must be an integer. This is a hint to the engine that it need not use a float here. If the rest of the code (usually, within a function) also does this in every place where `Number` semantics would otherwise require conversion back to a float, optimized engines notice that they can use 32-bit integers exclusively (without changing the semantics), which is faster. –  Mar 01 '23 at 02:12