Well having grown up in C++, I am always conscious about what algorithm would fit what. So when I notice the application started to behave sluggish on mobile phones I immediately started looking at the data structures and how they are represented.
I am noticing a very strange effect Array.includes
is an order of Magnitude faster than Set.has
. Even though Set.has
much more potential to be optimized for lookup: it's the whole idea of using a set.
My initialize code is (this code is outside the timing for the tests):
function shuffle(a) {
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
}
const arr = []
for (let i = 0; i < 1000; i+=1) {
arr.push(i);
};
shuffle(arr);
const prebuildset=new Set(arr);
And the tests are:
(new Set(arr)).has(-1); //20.0 kOps/s
arr.includes(-1); //632 kOps/s
(new Set(arr)).has(0); //20.0 kOps/s
arr.includes(0); //720 kOps/s
prebuildset.has(-1); //76.7 kOps/s
prebuildset.has(0); //107 kOps/s
Tested with chrome 73.0.3683.103 on Ubuntu 18.04 using https://jsperf.com/set-array-has-test/1
I kind of can expect the versions that create a set on the fly to be slower than directly testing an array for inclusion. (Though I'd wonder why chrome doesn't JIT optimize the array away - I've also tested using a literal array and a literal array vs using a variable doesn't matter at all in speed). However even prebuild sets are an order of magnitude slower than an array-inclusion test: even for the most negative case (entry not inside the array).
Why is this? What kind of black magic is happening?
EDIT: I've updated the tests to shuffle the results so as to not skew too much to an early stoppage for array.includes()
- While no longer 10 times as slow it is still many times slower, very relevant and out of what I expect it to be.