lodash some 方法性能为什么比 js 原生方法 还高?

2022-08-01 18:30:09 +08:00
 lqzhgood

今天写一个性能敏感的函数发现的这个有趣结果,lodash some 的性能是 js some 性能的几倍。 我觉得标题加个 [震惊] 都不为过~ /dogo

测试代码

const testArr = new Array(50_000_000).fill({ a: 1, b: 2, c: 3 });

console.time('es');
const x = testArr.some(v => v.a === 9 && v.b === 9 && v.c === 9);
console.timeEnd('es');

console.time('lodash');
const y = _.some(testArr, v => v.a === 9 && v.b === 9 && v.c === 9);
console.timeEnd('lodash');

// es: 590.248046875 ms
// lodash: 219.496826171875 ms

可以在 https://lodash.com/ 的 F12 中直接测试,我在 node16 环境下结果也一致,lodash-some 性能是 js-some 的几倍

js some

按我理解 js RunTime 应该是更高性能语言的实现(如 C 等),那么原生 some 方法性能应该更高呀。

[].some  --> ƒ some() { [native code] }

lodash some

lodash 的 some 源码在这 https://github.com/lodash/lodash/blob/master/some.js,也仅仅是很普通的 while 遍历,不知道为啥性能这么好。

5193 次点击
所在节点    JavaScript
27 条回复
hangbale
2022-08-01 18:39:48 +08:00
有没有可能 lodash 的 some 只实现了原生 some 的一部分功能
noe132
2022-08-01 18:44:57 +08:00
我的测试结果跟你不太一样
node v16.15.1

> a()
es: 206.54ms
lodash: 240.6ms
> a()
es: 211.843ms
lodash: 245.908ms
> a()
es: 212.926ms
lodash: 245.313ms
> a()
es: 210.621ms
lodash: 241.171ms
> a()
es: 212.199ms
lodash: 239.314ms
Leviathann
2022-08-01 18:45:45 +08:00
https://selfrefactor.github.io/rambda/#/?id=%e2%9d%af-benchmarks
https://mobily.github.io/ts-belt/benchmarks/v3.12.0/macbook-pro-2021

这两个大部分函数比 lodash 又快了不少
js 很多原生的 api 就是很慢的
lqzhgood
2022-08-01 18:47:40 +08:00
@noe132 我也是 node 16.15.1

es: 738.817ms
lodash: 205.519ms
lingly02
2022-08-01 18:47:50 +08:00
// Production steps of ECMA-262, Edition 5, 15.4.4.17
// Reference: http://es5.github.io/#x15.4.4.17
if (!Array.prototype.some) {
Array.prototype.some = function(fun/*, thisArg*/) {
'use strict';

if (this == null) {
throw new TypeError('Array.prototype.some called on null or undefined');
}

if (typeof fun !== 'function') {
throw new TypeError();
}

var t = Object(this);
var len = t.length >>> 0;

var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
for (var i = 0; i < len; i++) {
if (i in t && fun.call(thisArg, t[i], i, t)) {
return true;
}
}

return false;
};
}

这是原生代码的逻辑,是要比 lodash 复杂
mxT52CRuqR6o5
2022-08-01 18:55:15 +08:00
你去 lodash 官网,在控制台,先运行
const testArr = new Array(50_000_000).fill({ a: 1, b: 2, c: 3 });
再运行
console.time('es');
const x = testArr.some(v => v.a === 9 && v.b === 9 && v.c === 9);
console.timeEnd('es');

console.time('lodash');
const y = _.some(testArr, v => v.a === 9 && v.b === 9 && v.c === 9);
console.timeEnd('lodash');
就会发现 es 跑得更快了,放一起运行估计是受 gc 影响了
mxT52CRuqR6o5
2022-08-01 19:06:02 +08:00
在 chrome 各种试验,结果很不稳定,一会儿 es 快一会儿 lodash 快
firefox 的性能比较稳定,哪个在前面哪个耗时多,es 和 lodash 切换一下顺序速度就不一样了
lqzhgood
2022-08-01 20:04:31 +08:00
@mxT52CRuqR6o5 你说的 GC 确实有可能
因此按你说的两步执行。

第一步执行 testArr

然后第二步改成 setTime 延迟执行

``` js

setTimeout(() => {
console.time('lodash');
const y = _.some(testArr, v => v.a === 9 && v.b === 9 && v.c === 9);
console.log('y', y);
console.timeEnd('lodash');
}, 10 * 1000);

setTimeout(() => {
console.time('es');
const x = testArr.some(v => v.a === 9 && v.b === 9 && v.c === 9);
console.log('x', x);
console.timeEnd('es');
}, 20 * 1000);

```

无论 lodash 放前放后
lodash 都比 js 快 2~3 倍 不知道是不是和平台有关系

i7-7700HQ Chrome 103.0.5060.134
ragnaroks
2022-08-01 20:34:47 +08:00
先内置再 lodash:
es: 280.18994140625 ms
lodash: 86.681884765625 ms

CTRL+R 后交换顺序:
lodash: 219.944091796875 ms
es: 285.6279296875 ms

说实话写了这么多年 js/ts 从没在意过这个,而且我一向是能用内置就不用 lodash ,看来以后要改观了
mxT52CRuqR6o5
2022-08-01 20:35:22 +08:00
@lqzhgood 你在 codesandbox 里跑跑看,不要打开控制台,打开控制台浏览器会有一些额外工作影响测量准确性
codehz
2022-08-01 20:46:46 +08:00
其实 v8 的很多数组方法都是 js 写的 - 只是标记成 native ,毕竟无论如何都要做各种类型检查和转换(以及按 es 语义调用 proxy getter setter ),加上 es 语义允许数组方法在非数组类型上调用,native 写不会有多少优势 - 反而可能会漏掉一些语义保证)
lizhenda
2022-08-01 21:06:05 +08:00
平常还真不会注意,原生居然慢 ...
Huelse
2022-08-01 22:56:11 +08:00
我反复跑了几次的确 lodash 成绩稍好,第一次跑的话 lodash 只要 92ms

lodash: 228.31201171875 ms

es: 292.26806640625 ms

从表现上来看我觉得和申请内存有关
vace
2022-08-02 00:36:30 +08:00
lodash 不用考虑各种参数的类型检查,默认用户传入的参数都是有效的。
可以看 v8 实现的源码细节: https://chromium.googlesource.com/external/v8/+/refs/heads/master/src/array.js
autoxbc
2022-08-02 04:15:14 +08:00
引擎底层敢用 JS 解释 JS 说明 JIT 性能够好,这是好事
caisanli
2022-08-02 08:00:39 +08:00
while 比 for 执行更快?
loolac
2022-08-02 08:30:11 +08:00
es: 215.10009765625 ms
loolac
2022-08-02 08:30:17 +08:00
lodash: 173.812744140625 ms
bthulu
2022-08-02 09:14:31 +08:00
i5 8300H, lodash 比原生快 4-5 倍的样子
lqzhgood
2022-08-02 09:50:34 +08:00
@ragnaroks 我也是一样,能用原生实现尽量用原生(一是洁癖,二是觉得原生性能最优),现在在一些性能优先的函数可能要额外考虑考虑了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/870035

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX