Power of expression vs. Performances
map
and reduce
are the basic building blocks of functional programming.
The essence of map
and reduce
is that they give you - the developer - more power of expression over the equivalent piece of code using a while
or a for
loop.
And the power of expression is an important part of SW development.
But very often, there is a trade-off between power of expression and performances.
It’s the case with reduce
in javascript
.
But, fortunately, Clojurescript
comes to the rescue with its super-powerful macros and we - the clojurescript developers - enjoy both high performances an the power of expression.
In this article, we show illustrate this idea dealing with a function that calculates the sum of the squares of an array of numbers.
javascript: reduce vs. for loop
Let’s compare the performances of reduce
and for
for sum-of-squares
, with KLIPSE.
In this article, we are going to mix clojurescript
and javascript
interactive code snippets.
All the code snippets of this page are live and interactive powered by the klipse plugin:
- Live: The code is executed in your browser
- Interactive: You can modify the code and it is evaluated as you type
First, the reduce
implementation:
function sum_of_squares_reduce_js(arr) {
return arr.reduce(function(res, val) {
return res + val * val;
}, 0);
}
sum_of_squares_reduce_js([1,2,3])
And the for loop
implementation:
function sum_of_squares_for_js(arr) {
var res=0;
for(var i=0; i < arr.length; i++){
res = res + arr[i] * arr[i];
}
return res;
}
sum_of_squares_for_js([1,2,3])
Now, let’s compare the performances of reduce
vs. for
:
(def n 1000000)
(def js-arr (to-array (range n)))
(time (js/sum_of_squares_reduce_js js-arr))
(time (js/sum_of_squares_for_js js-arr))
In
javascript
(at least in Chrome on August 2016),reduce
is much slower thanfor
.
Feel free to modify js-arr
to benchmark as you wish.
Someone wrote in the comments that on Firefox, reduce
is faster than for
.
If you are on another browser, please write in the comments what are the performances on your browser….
Clojurescript areduce
In clojurescript
there is a handy way to reduce over native arrays.
Please welcome areduce.
With areduce
you pass an expression instead of a function
(areduce a idx ret init expr)
areduce
reduces an expression across an arraya
, using an index namedidx
, and return value namedret
, initialized toinit
, settingret
to the evaluation ofexpr
at each step, returningret
.
Obviously, areduce
is a macro.
It sounds complex, but with an example it will be very clear.
Let’s implement our sum of squares using areduce
and have a look at the transpiled javascript
code:
(defn sum-of-squares-clj-areduce [xs]
(areduce xs i ret 0
(+ ret (* (aget xs i) (aget xs i)))))
Observe the transpiled javascript
code: It’s very close to the implementation, you’d have written for sum-of-squares
, before reduce
was available in javascript
.
(Maybe you’d chosen better names for the variables.)
From the power of expression perspective, areduce
is as powerful are reduce
.
We will investigate the performance of areduce
in the next paragraph.
Clojurescript areduce vs. Javascript reduce
Now, it’s obvious why areduce
is faster that javascript
’s reduce
: areduce
is a macro that allows you - the developer - to write elegant code that is translated at compile time to fast native code.
Let’s see it in action with KLIPSE.
(defn sum-of-squares-clj-areduce [xs]
(areduce xs i ret 0
(+ ret (* (aget xs i) (aget xs i)))))
(time (sum-of-squares-clj-areduce js-arr))
In
clojurescript
,areduce
is as fast asfor
injavascript
Clojurescript rocks!