Yesterday, while profiling one of our NodeJS apps, I
bumped into an interesting piece of code that seemed to take too long to run:
interestingly enough, everything seemed to point to lodash’s pick
function.
For those of you who don’t know what that function does, it basically creates a new object from an existing one, picking only a user-defined list of properties to “move over”.
Easier in code than with words:
1 2 3 |
|
It’s kind of an elegant solution over doing these kind of operations manually:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
It doesn’t really change much in terms of style although, in my opinion, it looks a bit more elegant and lets you type less code.
So what about the performance impact? I wrote a small script to test it over a long list of “user” objects:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
So, we’re creating a list of a thousand objects with a handful of properies, loop through that list and map them, hand-picking a couple properies. The result:
1 2 3 4 |
|
What the actual [expletive] is going on here?
Well, Lodash needs to loop through all the keys in the object to cherry-pick the specific properties and, additionally, “needs” to call a whole bunch of functions:
pick
basePick
basePickBy
hasIn
hasPath
castPath
toKey
…and I honestly lost the count by then — I wonder what that does to the stack :)
So, even though I can’t pinpoint the exact bottleneck, I think there are a bunch of different factors that make its implementation way slower compared to using some more tedious, vanilla JS: the Big O is in itself less efficient, plus the fact that it needs to call a few different functions and do some sanity checks around definitely play a role in the slowdown.
In our case we used lodash in a loop that, worst-case scenario, involved ~100 items:
1 2 3 4 5 |
|
Guess how much time did we save in our app? Exactly 5ms.
To many, that might not seem too much, but considering that the response time of the given service was around 44ms, we basically cut it down by 10%.
Does this mean that Lodash suck?
Heck, no! How would you do this?
1 2 3 4 5 6 |
|
As always, there’s a use case for everything1, so you just need to understand
if the function, API or library you’re using is the best fit in your scenario:
for us, since we always knew the properties to pick in advance, using _.pick
and all the power it offers didn’t make a lot of sense.
Too often we rely on libraries and abstractions, thinking we’re never going to “pay” for that: granted that many times these abstractions are necessary so that we don’t re-invent the wheel and rely on someone else’s great work, we still need to evaluate what consequences using a framework or library brings to the table.
Cheers!
- Okay, maybe requiring JSON files from the filesystem based on user-input pushes it too far… ↩