How fast or slow are common mathematical operations to execute in a web browser or Node.js? How slow is a division when compared to a multiplication or a sine function compared to a square root? This article aims to find rough estimates for various elementary operations to aid algorithmic design and implementations that prioritise computational efficiency.
The reader should note that computation times vary between programming languages, interpreters, compilers, and devices. The surrounding code matters too, because the compilers and interpreters try to optimise the code in many complex ways . Also, often a designer of an algorithm is more interested on the computational complexity of the algorithm, referring to the effect of the input size in respect to execution time and memory usage . On the other hand, in situations where a piece of code is executed thousands or millions of times per second, knowledge on costs of the basic operations becomes handy.
Our benchmark source code is released as a GitHub repository . See the repository for implementation details. The details of the used benchmark environment are given later in this article. Below are the results after running the benchmark three times:
Operation Run 1 Run 2 Run 3 +-------------------+--------+--------+--------+ Baseline x => 0 0.27 ms 0.26 ms 0.25 ms x => x 0.32 ms 0.31 ms 0.30 ms Addition x => 3 + x 1.03 ms 1.02 ms 1.00 ms x => 3 - n 1.02 ms 1.05 ms 0.99 ms x => -x 1.04 ms 1.03 ms 0.98 ms Multiplication x => 3 * x 1.03 ms 1.02 ms 1.00 ms x => x * x 1.04 ms 1.02 ms 0.97 ms x => 3 / n 1.04 ms 1.04 ms 1.01 ms Exponentiation x => Math.sqrt(x) 1.13 ms 1.09 ms 1.07 ms x => Math.exp(x) 2.06 ms 2.05 ms 2.02 ms x => Math.pow(x,3) 2.83 ms 2.87 ms 2.84 ms Logarithms x => Math.log(x) 1.89 ms 1.94 ms 1.86 ms x => Math.log2(x) 1.91 ms 1.91 ms 1.87 ms x => Math.log10(x) 1.91 ms 1.97 ms 1.91 ms Rounding x => Math.ceil(x) 1.00 ms 0.96 ms 0.96 ms x => Math.floor(x) 0.95 ms 0.94 ms 0.92 ms x => Math.round(x) 1.05 ms 1.06 ms 1.02 ms Trigonometry x => Math.sin(x) 2.09 ms 2.14 ms 2.08 ms x => Math.cos(x) 2.10 ms 2.12 ms 2.08 ms x => Math.asin(x) 1.66 ms 1.71 ms 1.62 ms x => Math.sinh(x) 2.18 ms 2.20 ms 2.13 ms x => Math.tan(x) 2.28 ms 2.35 ms 2.25 ms x => Math.atan(x) 2.00 ms 2.00 ms 1.98 ms
During a single run, each operation is executed around 500 times for an array of 10000 random numbers. The numbers in the array are floats within the range -100..100. The reported times tell how much the mapping of the 10000 elements took in average.
To analyse the raw results, let us compare the groups. Addition, multiplication, and rounding took roughly equal time. Logarithms and trigonometric functions took roughly twice as much time. Surprisingly the exponentiation group of operations
pow was more divided. The square root was almost on a par with multiplication and e to the power of x on a par with logarithms. Taking x to the power of 3 was the slowest operation of the benchmark and took almost three times longer than multiplication and division.
Let us then assume the baseline
x => x captures the time required for the things other that the actual operation, like variable declaration and memory management. Therefore, to get a better relative comparison between the operations, we should first normalise their times by subtracting the baseline. For that, let us use a relative unit of time, U, instead of actual milliseconds. In our case, 1 ms ~ 3 U and the baseline is 1 U. For example, the addition took ~1 ms = 3 U and after the baseline normalisation its run time becomes 2 U.
Now we summarise the results by normalising and sorting them in ascending order:
■■ addition, multiplication, and division took 2 U
■■ ceil, floor, and round: 2 U
■■ square root: 2 U
■■■■ asin: 4 U
■■■■■ exp and logarithms: 5 U
■■■■■ sin, cos, atan, sinh: 5 U
■■■■■■ tan: 6 U
■■■■■■■■ pow: 8 U
The environment used for the benchmark was:
– Mac mini (Late 2012)
– 2,5 GHz Intel Core i5
– macOS Mojave 10.14.6
– Node.js 14.5.0
We understand that to get general and reliable results, the benchmark should be averaged over various environments. Lacking resources to do so, we settle to comparing them to a couple of earlier benchmarks.
Comparison with earlier benchmarks
Let us summarise the earlier benchmarks by Atkinson  and Wernersson  and then compare them to our results above. Because the benchmark results are relative to the processing speed and the number of iterations, we again use the relative a unit of time, U, and note that it is only comparable within the same benchmark.
Our rough summary of the simple C++ benchmark by Atkinson:
■ addition and multiplication took 1 U
■■■■ division: 4 U
■■■■■■ square root: 6 U
■■■■■■■■■ exp: 9 U
■■■■■■■■■■■■■■ sin, cos: 14 U
■■■■■■■■■■■■■■■■■■■ tan: 19 U
■■■■■■■■■■■■■■■■■■■■■■■ atan: 23 U
Our rough summary of the broader C++ benchmark by Wernersson:
■ floor, ceil, addition, multiplication, and division took 1 U
■■ square root: 2 U
■■■■■■■■ round: 8 U
■■■■■■■■■■■■■■ exp: 14 U
■■■■■■■■■■■■■■■ sin: 15 U
■■■■■■■■■■■■■■■■■■■ asin, sinh: 19 U
■■■■■■■■■■■■■■■■■■■■■■ atan, cube root: 22 U
■■■■■■■■■■■■■■■■■■■■■■■■■ tan: 25 U
■■■■■■■■■■■■■■■■■■■■■■■■■■ log, log10: 26 U
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ pow: 62 U
By comparing the C++ benchmarks to our Node.js benchmark, we can conclude that the general order of the operations is mostly the same. Addition, multiplication, and division are the cheapest of the operations. Then comes square root and exp, followed by logarithms and trigonometric functions, and finally the most expensive of them all, pow.
To end with a few subjective remarks, we were surprised by the high cost of taking x to the power of n. No wonder why
pow(x, 2) is often replaced by
x * x. We were also surprised by relative cheapness of
sqrt. Before the benchmark we assumed it to be on a par with
 Wikipedia. Computational complexity. 2021.
 L. Atkinson. A simple benchmark of various math operations. 2014.
 E. Wernersson. math_ops_speed: testing speed of mathematical operators. GitHub repository. 2020.