So when I said above that
from queries would be blazing fast, I meant it, and to prove it I've done a quick benchmark of other similar solutions:
let fromQuery = new Query()
.where(it => it % 2 === 0)
.take(10000)
.select(it => it + 1)
.reduce((a, it) => a + it, 0);
event count time (us) % run avg (us) % avg |
------------------------------------------------------------------------
Array method chain 1,001 3,605,798 34.1 % 3,602 34.3 % |
Underscore chain 1,001 2,065,016 19.5 % 2,062 19.6 % |
Lodash chain 1,001 1,168,390 11.0 % 1,167 11.1 % |
from.js 1.0 1,001 903,805 8.5 % 902 8.6 % |
from.ts (Oozaru) 1,001 875,786 8.3 % 874 8.3 % |
Link.js query 1,001 475,447 4.5 % 474 4.5 % |
Link.js query (NR) 1,001 325,084 3.1 % 324 3.1 % |
from.js 2.0 1,001 319,423 3.0 % 319 3.0 % |
Lazy.js sequence 1,001 270,130 2.6 % 269 2.6 % |
Sphere from() query 1,001 205,757 1.9 % 205 2.0 % |
Sphere Query object 1,001 188,966 1.8 % 188 1.8 % |
handwritten 'for' loop 1,001 119,092 1.1 % 118 1.1 % |
Sphere from() query and
Sphere Query object is us! All timings are for running a chain equivalent to the above query 1,000 times with the respective library over an array of 100,000 random integers between 0 and 1000.
Special thanks to
@Radnen for (indirectly) giving me the idea - Link.js was touted as a replacement for writing repetitive
for loops, which got me to thinking... what if I just compile the query to
an actual for loop...This is really incredible that I was able to get so close to native loop performance and is a big win for code readability. Query chains remain understandable even with 10+ query operators chained together, but throw together a couple filters and mappings plus a sort (or two!) and the set of
for loops you need to write to match it can get pretty gnarly. Lodash is proof people are willing to sacrifice a great deal of performance to get more readable code (see benchmark results above), even in tight loops where it matters most, but it's even better if you don't have to.