How bitwise operations boost performance in Asm.js? - javascript

At the first lines of Asm.js definition there's a Asm.js-based code example that explains the bitwise operation helps to have a faster JS code:
HEAP32[p >> 2]|0
or
(x+y)|0
My question is, how this operation boost the performance and what's the reason behind using this bitwise operator many times in Asm.js or Emscripten-generated JS codes?

The bitwise operators force their operands to be integer values. It's a considerably faster way of doing the conversion than calling Math.floor, etc. Note that
p >> 2
is (for non-negative values of p) the same as Math.floor(p / 4).

Using a right bit shift (>>) by 1, is the same as dividing by 2, but it is faster than actually calculating it base 10 because the computer can just shift the binary digits in memory without having to do a calculation. It's just like in base 10 when you want to multiply a number by 10, you know you can just add a zero to the right side of the number (The reverse, division, would be taking the zero off of the right side). So, by shifting right two digits you are dividing by 2 twice (e.g. 24/2 = 12 and 12/2 = 6 or 24/4 = 6)
Apparently, (x+y)|0 is just a faster way to do a floor.

Related

Faster way to multiply in javascript?

I'm looking at ways to multiply faster in JavaScript, and I found this
https://medium.com/#p_arithmetic/4-more-javascript-hacks-to-make-your-javascript-faster-1f5fd88a219e#.306ophng2
which has this text
Instead of multiplying, use the bit shift operation. it looks a little more complex, but once you get the hang of it, it’s pretty simple. The formula to multiply x * y is simply x << (y-1)
As a bonus, everyone else will think you’re really smart!
// multiply by 1
[1,2,3,4].forEach(function(n){ return n<<0; }) // 1,2,3,4
// multiply by 2
[1,2,3,4].forEach(function(n){ return n<<1; }) // 2,4,6,8
// multiply by 3
[1,2,3,4].forEach(function(n){ return n<<2; }) // 3,6,9,12
// etc
However this doesn't seem to work for me. Does anyone know what's wrong?
Thanks
Ignore that article. It's intended as humor -- the advice it's giving is intentionally terrible and wrong.
Multiplication does not involve "expensive logarithmic look up tables and extremely lucky guesses" with "dozens of guesses per second". It is a highly optimized hardware operation, and any modern CPU can perform hundreds of millions (or more!) of these operations per second.
Bitwise operations are not faster than multiplication in Javascript. In fact, they're much slower -- numbers are generally stored as double-precision floating point by default, so performing a bitwise operation requires them to be converted to integers, then back.
Bit-shifting is not equivalent to multiplication in the way that the article implies. While left-shifting by 0 and 1 are equivalent to multiplication by 1 and 2, the pattern continues with <<2 and <<3 being equivalent to a multiplication by 4 and 8, not 3 and 4.
Array.forEach does not return a value. The appropriate function to use here would be Array.map.
The other "Javascript hacks" described in the article are even worse. I won't bother going into details.
Bit shift is not the same as multiplying by any number. This is how bit shift works in decimal for example:
1234 >> 0 = 1234
1234 >> 1 = 0123 #discards the last digit.
0123 >> 2 = 0001 #discards the last two digit
So in decimal, shifting digits to the right is like integer division by 10. When shifted by two digits it's the same as dividing by 10^2.
Now computers work with binary words. So instead of a digit shift, we have bit shifts.
10101011 >> 1 = 01010101
This is basically dividing by 2^1.
10101011 >> n is simply dividing by 2^n.
Now in the same manner,
10101011 << n is multiplying by by 2^n. This adds n zeroes behind the binary word.
Hope it's clearer now :)
Cheers
Incidentally, if you want to know why it isn't working for you, you will want to use map instead of forEach since forEach ignores the return.
So do this instead:
// multiply by 1
[1,2,3,4].map(function(n){ return n<<0; }) // 1,2,3,4
// multiply by 2
[1,2,3,4].map(function(n){ return n<<1; }) // 2,4,6,8
// multiply by 4
[1,2,3,4].map(function(n){ return n<<2; }) // 4,8,12,16
// multiply by 8
[1,2,3,4].map(function(n){ return n<<3; }) // 8, 16, 24, 32
And as others have mentioned - don't use this for multiplication. Just take this as a clarity of forEach vs map...

Preserving the floating point & addition of a bitwise operation in javascript

I am trying to understand the way to add, subtract, divide, and multiply by operating on the bits.
It is necessary to do some optimizing in my JavaScript program due to many calculations running after an event has happened.
By using the code below for a reference I am able to understand that the carry holds the &ing value. Then by doing the XOr that sets the sum var to the bits that do not match in each n1 / n2 variable.
Here is my question.;) What does shifting the (n1 & n2)<<1 by 1 do? What is the goal by doing this? As with the XOr it is obvious that there is no need to do anything else with those bits because their decimal values are ok as they are in the sum var. I can't picture in my head what is being accomplished by the & shift operation.
function add(n1,n2)
{
var carry, sum;
// Find out which bits will result in a carry.
// Those bits will affect the bits directly to
// the left, so we shall shift one bit.
carry = (n1 & n2) << 1;
// In digital electronics, an XOR gate is also known
// as a quarter adder. Basically an addition is performed
// on each individual bit, and the carry is discarded.
//
// All I'm doing here is applying the same concept.
sum = n1 ^ n2;
// If any bits match in position, then perform the
// addition on the current sum and the results of
// the carry.
if (sum & carry)
{
return add(sum, carry);
}
// Return the sum.
else
{
return sum ^ carry;
};
};
The code above works as expected but it does not return the floating point values. I've got to have the total to be returned along with the floating point value.
Does anyone have a function that I can use with the above that will help me with floating point values? Are a website with a clear explanation of what I am looking for? I've tried searching for the last day are so and cannot find anything to go look over.
I got the code above from this resource.
http://www.dreamincode.net/code/snippet3015.htm
Thanks ahead of time!
After thinking about it doing a left shift to the 1 position is a multiplication by 2.
By &ing like this : carry = (n1 & n2) << 1; the carry var will hold a string of binaries compiled of the matched positions in n1 and n2. So, if n1 is 4 and n2 is 4 they both hold the same value. Therefore, by combing the two and right shifting to the 1 index will multiply 4 x 2 = 8; so carry would now equal 8.
1.) var carry = 00001000 =8
&
00001000 =8
2.) carry = now holds the single value of 00001000 =8
A left shift will multiply 8 x 2 =16, or 8 + 8 = 16
3.)carry = carry <<1 , shift all bits over one position
4.) carry now holds a single value of 00010000 = 16
I still cannot find anything on working with floating point values. If anyone has anything do post a link.
It doesn't work because the code assumes that the floating point numbers are represented as integer numbers, which they aren't. Floating point numbers are represented using the IEEE 754 standard, which breaks the numbers in three parts: a sign bit, a group of bits representing an exponent, and another group representing a number between 1 (inclusive) and 2 (exclusive), the mantissa, and the value is calculated as
(sign is set ? 1 : -1) * (mantissa ^ (exponent - bias))
Where the bias depends on the precision of the floating point number. So the algorithm you use for adding two numbers assumes that the bits represent an integer which is not the case for floating point numbers. Operations such as bitwise-AND and bitwise-OR also don't give the results that you'd expect in an integer world.
Some examples, in double precision, the number 2.3 is represented as (in hex) 4002666666666666, while the number 5.3 is represented as 4015333333333333. OR-ing those two numbers will give you 4017777777777777, which represents (roughly) 5.866666.
There are some good pointers on this format, I found the links at http://www.psc.edu/general/software/packages/ieee/ieee.php, http://babbage.cs.qc.edu/IEEE-754/ and http://www.binaryconvert.com/convert_double.html fairly good for understanding it.
Now, if you still want to implement the bitwise addition for those numbers, you can. But you'll have to break the number down in its parts, then normalize the numbers in the same exponent (otherwise you won't be able to add them), perform the addition on the mantissa, and finally normalize it back to the IEEE754 format. But, as #LukeGT said, you'll likely not get a better performance than the JS engine you're running. And some JS implementations don't even support bitwise operations on floating point numbers, so what usually ends up happening is that they first cast the numbers to integers, then perform the operation, which will make your results incorrect as well.
Floating point values have a complicated bit structure, which is very difficult to manipulate with bit operations. As a result, I doubt you could do any better than the Javascript engine at computing them. Floating point calculations are inherently slow, so you should try to avoid them if you're worried about speed.
Try using integers to represent a decimal number to x amount of digits instead. For example if you were working with currency, you could store things in terms of whole cents as opposed to dollars with fractional values.
Hope that helps.

">>1" equals "/2"? [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
>> in javascript
Here:
var num=10;
console.log(num/2);
num=4;
console.log(num/2);
It gives me 5 and 2.
And this one:
var num=10;
console.log(num>>1);
num=4;
console.log(num>>1);
It also gives me 5 and 2.
So x/2 is the same as x>>1? But why?
For the same reason that dropping the last digit off a normal (decimal) number is the same as dividing it by 10 (ignoring, of course, any non-integer remainder).
In computers, integers are internally represented in binary (base 2). So each digit represents a power of 2 instead of a power of 10 that we're used to with the decimal system.
>> 1 just means to shift all the bits right by one, which is another way of saying "drop the last digit". Since the digits are in binary, that's equivalent to dividing by the base, which is 2.
Similarly, if you need to divide by any power of 2, you can do so using the right shift operator: To divide by 4, shift by 2; to divide by 8, shift by 3; and so on.
Note that internally, it's often more efficient to do a shift operation instead of a division operation, but any compiler worth its salt will do this optimization for you (so that you don't have to write obfuscated code to get the performance benefit -- generally, you would only use the shift operator when your intention is to manipulate bits directly, and use the division operator when your intention is to do math).
x>>1 is a bit shift, which operates on the number's binary representation. The effect is that x>>n is the same as x/(2^n) (except that bit shift is usually faster than division, as it is lower level).
When you >> something, you basically shift all its bits to the right.
When that happens, you shift the 2-place value into the 1-place value, and the 4-place value into the 2-place value, and so on.
This effectively divides a number in half.
Take the number 14, for example:
1110.
When you shift the bits, you get 111, or 7.
Take a look at this:
http://en.wikipedia.org/wiki/Division_by_two#Binary
Everything you need to know about >> can be found here and here.

48-bit bitwise operations in Javascript?

I've been given the task of porting Java's Java.util.Random() to JavaScript, and I've run across a huge performance hit/inaccuracy using bitwise operators in Javascript on sufficiently large numbers. Some cursory research states that "bitwise operators in JavaScript are inherently slow," because internally it appears that JavaScript will cast all of its double values into signed 32-bit integers to do the bitwise operations (see here for more on this.) Because of this, I can't do a direct port of the Java random number generator, and I need to get the same numeric results as Java.util.Random(). Writing something like
this.next = function(bits) {
if (!bits) {
bits = 48;
}
this.seed = (this.seed * 25214903917 + 11) & ((1 << 48) - 1);
return this.seed >>> (48 - bits);
};
(which is an almost-direct port of the Java.util.Random()) code won't work properly, since Javascript can't do bitwise operations on an integer that size.)
I've figured out that I can just make a seedable random number generator in 32-bit space using the Lehmer algorithm, but the trick is that I need to get the same values as I would with Java.util.Random(). What should I do to make a faster, functional port?
Instead of foo & ((1 << 48) - 1) you should be able to use foo % Math.pow(2,48).
All numbers in Javascript are 64-bit floating point numbers, which is sufficient to represent any 48-bit integer.
An alternative is to use a boolean array of 48 booleans, and implement the shifting yourself. I don't know if this is faster, though; but I doubt it, since all booleans are stored as doubles.
Bear in mind that a bit shift is directly equivalent to a multiplication or division by a power of 2.
1 << x == 1 * Math.pow(2,x)
It is slower than bit shifting, but allows you to extend beyond 32 bits. It may be a faster solution for bits > 32, once you factor in the additional code you need to support higher bit counts, but you'll have to do some profiling to find out.
48-bit bitwise operations are not possible in JavaScript. You could use two numbers to simulate it though.

2.9999999999999999 >> .5?

I heard that you could right-shift a number by .5 instead of using Math.floor(). I decided to check its limits to make sure that it was a suitable replacement, so I checked the following values and got the following results in Google Chrome:
2.5 >> .5 == 2;
2.9999 >> .5 == 2;
2.999999999999999 >> .5 == 2; // 15 9s
2.9999999999999999 >> .5 == 3; // 16 9s
After some fiddling, I found out that the highest possible value of two which, when right-shifted by .5, would yield 2 is 2.9999999999999997779553950749686919152736663818359374999999¯ (with the 9 repeating) in Chrome and Firefox. The number is 2.9999999999999997779¯ in IE.
My question is: what is the significance of the number .0000000000000007779553950749686919152736663818359374? It's a very strange number and it really piqued my curiosity.
I've been trying to find an answer or at least some kind of pattern, but I think my problem lies in the fact that I really don't understand the bitwise operation. I understand the idea in principle, but shifting a bit sequence by .5 doesn't make any sense at all to me. Any help is appreciated.
For the record, the weird digit sequence changes with 2^x. The highest possible values of the following numbers that still truncate properly:
for 0: 0.9999999999999999444888487687421729788184165954589843749¯
for 1: 1.9999999999999999888977697537484345957636833190917968749¯
for 2-3: x+.99999999999999977795539507496869191527366638183593749¯
for 4-7: x+.9999999999999995559107901499373838305473327636718749¯
for 8-15: x+.999999999999999111821580299874767661094665527343749¯
...and so forth
Actually, you're simply ending up doing a floor() on the first operand, without any floating point operations going on. Since the left shift and right shift bitwise operations only make sense with integer operands, the JavaScript engine is converting the two operands to integers first:
2.999999 >> 0.5
Becomes:
Math.floor(2.999999) >> Math.floor(0.5)
Which in turn is:
2 >> 0
Shifting by 0 bits means "don't do a shift" and therefore you end up with the first operand, simply truncated to an integer.
The SpiderMonkey source code has:
switch (op) {
case JSOP_LSH:
case JSOP_RSH:
if (!js_DoubleToECMAInt32(cx, d, &i)) // Same as Math.floor()
return JS_FALSE;
if (!js_DoubleToECMAInt32(cx, d2, &j)) // Same as Math.floor()
return JS_FALSE;
j &= 31;
d = (op == JSOP_LSH) ? i << j : i >> j;
break;
Your seeing a "rounding up" with certain numbers is due to the fact the JavaScript engine can't handle decimal digits beyond a certain precision and therefore your number ends up getting rounded up to the next integer. Try this in your browser:
alert(2.999999999999999);
You'll get 2.999999999999999. Now try adding one more 9:
alert(2.9999999999999999);
You'll get a 3.
This is possibly the single worst idea I have ever seen. Its only possible purpose for existing is for winning an obfusticated code contest. There's no significance to the long numbers you posted -- they're an artifact of the underlying floating-point implementation, filtered through god-knows how many intermediate layers. Bit-shifting by a fractional number of bytes is insane and I'm surprised it doesn't raise an exception -- but that's Javascript, always willing to redefine "insane".
If I were you, I'd avoid ever using this "feature". Its only value is as a possible root cause for an unusual error condition. Use Math.floor() and take pity on the next programmer who will maintain the code.
Confirming a couple suspicions I had when reading the question:
Right-shifting any fractional number x by any fractional number y will simply truncate x, giving the same result as Math.floor() while thoroughly confusing the reader.
2.999999999999999777955395074968691915... is simply the largest number that can be differentiated from "3". Try evaluating it by itself -- if you add anything to it, it will evaluate to 3. This is an artifact of the browser and local system's floating-point implementation.
If you wanna go deeper, read "What Every Computer Scientist Should Know About Floating-Point Arithmetic": https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
Try this javascript out:
alert(parseFloat("2.9999999999999997779553950749686919152736663818359374999999"));
Then try this:
alert(parseFloat("2.9999999999999997779553950749686919152736663818359375"));
What you are seeing is simple floating point inaccuracy. For more information about that, see this for example: http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems.
The basic issue is that the closest that a floating point value can get to representing the second number is greater than or equal to 3, whereas the closes that the a float can get to the first number is strictly less than three.
As for why right shifting by 0.5 does anything sane at all, it seems that 0.5 is just itself getting converted to an int (0) beforehand. Then the original float (2.999...) is getting converted to an int by truncation, as usual.
I don't think your right shift is relevant. You are simply beyond the resolution of a double precision floating point constant.
In Chrome:
var x = 2.999999999999999777955395074968691915273666381835937499999;
var y = 2.9999999999999997779553950749686919152736663818359375;
document.write("x=" + x);
document.write(" y=" + y);
Prints out: x = 2.9999999999999996 y=3
The shift right operator only operates on integers (both sides). So, shifting right by .5 bits should be exactly equivalent to shifting right by 0 bits. And, the left hand side is converted to an integer before the shift operation, which does the same thing as Math.floor().
I suspect that converting 2.9999999999999997779553950749686919152736663818359374999999
to it's binary representation would be enlightening. It's probably only 1 bit different
from true 3.
Good guess, but no cigar.
As the double precision FP number has 53 bits, the last FP number before 3 is actually
(exact): 2.999999999999999555910790149937383830547332763671875
But why it is
2.9999999999999997779553950749686919152736663818359375
(and this is exact, not 49999... !)
which is higher than the last displayable unit ? Rounding. The conversion routine (String to number) simply is correctly programmed to round the input the the next floating point number.
2.999999999999999555910790149937383830547332763671875
.......(values between, increasing) -> round down
2.9999999999999997779553950749686919152736663818359375
....... (values between, increasing) -> round up to 3
3
The conversion input must use full precision. If the number is exactly the half between
those two fp numbers (which is 2.9999999999999997779553950749686919152736663818359375)
the rounding depends on the setted flags. The default rounding is round to even, meaning that the number will be rounded to the next even number.
Now
3 = 11. (binary)
2.999... = 10.11111111111...... (binary)
All bits are set, the number is always odd. That means that the exact half number will be rounded up, so you are getting the strange .....49999 period because it must be smaller than the exact half to be distinguishable from 3.
I suspect that converting 2.9999999999999997779553950749686919152736663818359374999999 to its binary representation would be enlightening. It's probably only 1 bit different from true 3.
And to add to John's answer, the odds of this being more performant than Math.floor are vanishingly small.
I don't know if JavaScript uses floating-point numbers or some kind of infinite-precision library, but either way, you're going to get rounding errors on an operation like this -- even if it's pretty well defined.
It should be noted that the number ".0000000000000007779553950749686919152736663818359374" is quite possibly the Epsilon, defined as "the smallest number E such that (1+E) > 1."

Categories

Resources