Javascript internal numeric, integer and float native type optimization

Javascript is not a strongly typed language, a variable can hold any data and there is no need to define a data type like in C or other languages. This however presents a problem for the Javascript Engine when working with numeric types. I will focus on two types of numbers - integers, whole numbers like 12345678 and decimal numbers, also called floating point numbers like 1565.4523454.

The central processing unit (CPU) has a component called an Arithmetic Logic Unit (ALU), that performs the calculations. In order to function it needs to know the type of the number it operates on. For instance in order to add two integers together there are a specific electronic components inside the ALU (etched in the silicon) responsible for performing only this type of operation. This component is called an Adder. These components can only sum up integers, they cannot sum up floating point numbers. There are other components inside the CPU responsible for summing up floating point numbers. Other numeric operations such multiplication, division, shift left, shift right and various bit-wise operations have their own electronic components inside the ALU.

Numerical operations on integers are much simpler to perform than operations of floating point numbers. Historically floating point operations were orders of magnitude slower than integer operations and this still remains the case today, but to a smaller extent. Older CPUs like Intel 486 usually did not have separate electronic components for floating point operations and were doing such operations by using the integer components. In modern CPUs floating point are fast but integer operations are still significantly faster in most cases.

Javascript Engines eventually need to run your Javascript code on the CPU and at that point it needs to know if it is an integer type or a floating point number. It would be wasteful to perform floating point multiplication instead of integer multiplication, floating point operations maybe 3 or more times slower than integer operations. Further distinctions are needed to understand the size of the variable, for instance operations on a 32bit integer are usually faster than operations on a 64bit integer and it would be much faster to use a 32bit integer when performing the operation on the CPU. Sometimes the engine may even change the internal type of a certain Javascript variable while the script is running as it is being assigned different values.

Common wisdom holds that Javascript numbers are always 64 bit floating point numbers, however this is not the case in modern Javascript Engines. A lot of improvements in modern Javascript Engines in the last few years have been from smarter usage of native datatypes. The problem with this is that the Javascript Engine in many cases must know ahead of time what kind of data a certain variable is going to have. If the data is dynamic the engine must assume the worst and use the widest, most general type possible, this is both wasteful and slow. The engine analyzes the Javascript before running it and tries to guess the most efficient type which is sufficient to hold the result of the calculation.

If we know that the types are integer we can help the engine by always removing the decimal parts when we assign data to the variable from an external source. The simplest way to do this is use the bit-wise operator or - |0, using the operator on an integer type with |0 does not change it's value but using it on floating point type truncates the decimal part and helps the Javascript Engine to use an efficient integer type internally for the result.

Now let's take a look at a comparative benchmark. Here I will run 50,000,000 cycles, each doing 10 numeric operations one on a variable that is only assigned integer values and one that is assigned decimal value 0.5 before the start of a loop. The loop code is exactly the same. On my machine the integer code is more than 5 times faster.

The Javascript Engine has to play it safe. If it suspects that anywhere in the program the variable may be assigned a floating point value it will use a floating point representation internally slowing down all other operations. For example in the following code 0.5 is added to the total if a certain flag variable is set to 1. This variable is set to 1 before the loop if Math.random() returns a value higher than 2, Math.random() only returns values between 0 and 1 so the flag variable is never set. However the Javascript Engine does not know this fact and assumes that the flag can be 1 and then continues to use a floating point variable for the entire loop.

Leave a Reply

Your email address will not be published. Required fields are marked *