goldbergyoni / goldbergyoni/nodebestpractices
Multiple problems in 7.2 (Prefer native JS methods over user-land utils like Lodash)
- Dominant language
- Dockerfile
- Stars
- 106k
- Forks
- 10.7k
- PR merge metrics
- No merged PRs in 30d
Description
https://github.com/goldbergyoni/nodebestpractices/blob/master/sections/performance/nativeoverutil.md
1. The first and most important problem is that the referenced benchmark results in https://github.com/Berkmann18/NativeVsUtils are two years old. Lodash, Underscore, and, most important, V8 engine in Node.js, have changed since. Developers of V8 have done many optimizations, e.g. some of the methods were initially implemented in self-hosted JS code and now are rewritten in C++. Therefore readers need more recent benchmark results.
2. Benchmarks in https://github.com/Berkmann18/NativeVsUtils contain many run-time bugs. Some tests cannot even start properly, giving us wrong decisions about who is faster in that category. I just accidentally spotted some of them:
- https://github.com/Berkmann18/NativeVsUtils/issues/2
- https://github.com/Berkmann18/NativeVsUtils/issues/3
- https://github.com/Berkmann18/NativeVsUtils/issues/4
The problem is visible even in the code example https://github.com/goldbergyoni/nodebestpractices/blob/master/sections/performance/nativeoverutil.md#code-example--benchmark-test-on-_concatarrayconcat - there is no benchmark data for `underscore` on the screenshot because there is no `concat` in Underscore! This example should definitely be fixed because posting code examples with errors cannot count as a best practice. Also, benchmarks should be checked for errors, fixed, and re-run.
3. The mean value is not suited well to represent benchmark results in languages like JS, where the code is initially interpreted, then optimized and executed much faster, and where the garbage collector always can significantly slow down some of the samples - all that easily affects the mean value. The 50th percentile (aka median) is way more useful because it is more resistant to such problems. I described [here](https://thinkjs.blogspot.com/2020/06/customize-output-of-benchmarkjs-results.html) how to calculate medians in Benchmark.js.
4. [One Paragraph Explainer](https://github.com/goldbergyoni/nodebestpractices/blob/master/sections/performance/nativeoverutil.md#one-paragraph-explainer) should be carefully reworked. There is a set of native alternatives, which should be used always instead of their Lodash alternatives because they are obviously faster, but there is a lot of cases, where the speed is about the same, and for that cases, Lodash could be preferred because it gives more readable and compact code. Also, there are cases when Lodash is still significantly faster, e.g. [_.uniq vs Set](https://github.com/Berkmann18/NativeVsUtils/blob/master/nativeVsLodash.js#L284-L286), [_.size vs Object.keys().length](https://github.com/Berkmann18/NativeVsUtils/blob/master/nativeVsLodash.js#L264-L266), [reverse](https://github.com/Berkmann18/NativeVsUtils/blob/master/nativeVsLodash.js#L193-L195), [fill](https://github.com/Berkmann18/NativeVsUtils/blob/master/nativeVsLodash.js#L122-L123), but again, results may change in the future versions of V8.
Anyways, the presented results are true only for that code with that short data samples. You could see different results when you benchmark your real code with your real data - that should be noted in the explainer.
5. Last but not least. Lodash methods like `filter` `map` should not be compared to their native counterparts in isolation, because Lodash supports [shortcut fusion](https://lodash.com/docs/4.17.15#lodash), e.g. `_(array).filter(fn1).map(fn2).take(5).value()` will actually iterate only once and only over the part of the array needed to produce 5 elements no matter how long array actually is (10000 entries? 1000000 entries?) and thus will have the complexity of O(1) (execution time is almost constant and does not depend on array length). The native solution will be either compact and ineffective (first filter() all the entries, then map(), and only then take the first 5 entries with the resulting complexity of O(N)) or verbose (merge filtering and mapping code together and implement early exit by hands). So Lodash offers the opportunity to easily write efficient array processing code.
Benchmark example: https://jsbench.me/43kgs6ox63/1 - try to play with array size and notice how numbers change.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.