After reading about transducers and why they’re awesome, I played around with some large arrays and measured their timings.
If you’re new to transducers I’ll quickly explain here, but highly recommend you read this intro.
Max 10 Million Salaries
Let’s make an array of 10 million users, each with a six-figure salary:
users = Array(10000000)
.fill()
.map((_, index) => ({
name: `User ${index + 1}`,
salary: Math.floor(Math.random() * 999999) + 100000
}));
Now we want to:
- Get the even-numbered salaries using
filter()
. - Calculate the weekly paycheck using
map()
. - Get the highest paycheck using
reduce()
.
We’ll focus on two implementations…
Method Chaining
As of this writing, method chaining’s more familiar to me.
map
, filter
, and reduce
are array methods. They also return an array, so we can chain them.
Let’s use console.time
and console.timeEnd
to measure how long the operation takes.
getMaxEvenPaycheck = (users) => {
console.time('Method chaining');
const max = users
.filter((user) => user.salary % 2 === 0)
.map((user) => user.salary / 52)
.reduce((a, b) => Math.max(a, b));
console.timeEnd('Method chaining');
return max;
};
Here’s our result:
This works just fine! The operation took about 3.56 seconds.
Our speed, however, can improve.
Since methods like map
, reduce
, and filter
return arrays, chaining them means you’re wasting time by creating intermediate arrays.
Transducers
Instead of intermediate arrays, a transducer processes one element at a time, like an assembly line.
We get the same result in less time.
My favorite library for this is RxJS. The concept is similar, but we’re operating on one element at a time.
getMaxEvenPaycheck = (users) => {
const { filter, map, max } = Rx.operators;
console.time('Transducer');
return Rx.Observable.from(users).pipe(
filter((user) => user.salary % 2 === 0),
map((user) => user.salary / 52),
max()
);
};
getMaxEvenPaycheck(users).subscribe((max) => {
console.log('max:', max);
console.timeEnd('Transducer');
});
By streaming users
and manipulating elements one-by-one with RxJS operators, our performance increased by about 75%!
Here’s the screenshots side by side, for reference:
I’m exploring this topic more, and would love any resources you could throw at me!