【问题标题】:When does for loop, .map() or .filter() have better performance?for 循环、.map() 或 .filter() 什么时候性能更好?
【发布时间】:2018-05-16 19:04:30
【问题描述】:

基于this 问题,想法是根据对象可能具有的某些属性值在对象数组中查找对象,因此我制作了这个脚本来检查哪种方法更快:

const elementsInObject = 1000000;
const lengthOfItems = 5;

//just random text
function randomText() {
    var text = "";
    var possible = "abcdefghijklmnopqrstuvwxyz";

    for (var i = 0; i < lengthOfItems; i++)
    text += possible.charAt(Math.floor(Math.random() * possible.length));

    return text;
}


//ccreation of object
console.time("creatingObject");
var arrObj = [];
for (let i = 0; i < elementsInObject; i++) {
  let el = {
    prop1: randomText(),
    prop2: randomText(),
    types: [randomText(), randomText()]
  };

  arrObj.push(el);
}
console.timeEnd("creatingObject");


console.time("forFor");
var result = [];
for (let i = 0; i < arrObj.length; i++) {

     for(let q=0; q<arrObj[i].types.length; q++){

        if (arrObj[i].types[q] == "abcde") {
            result.push(arrObj[i]);
          }

     }

    }
console.timeEnd("forFor");

console.time("forIncludes");
var result = [];
for (let i = 0; i < arrObj.length; i++) {
  if (arrObj[i].types.includes("abcde")) {
    result.push(arrObj[i]);
  }
}
console.timeEnd("forIncludes");

console.time("filterIncludes");
var result = arrObj.filter(obj => {
  return obj.types.includes("abcde");
});
console.timeEnd("filterIncludes");

console.time("mapIncludes");
var result = [];
arrObj.map(obj => {
  if (obj.types.includes("abcde")) {
    result.push(obj);
  }
});
console.timeEnd("mapIncludes");

console.time("filterIndexOf");
var result = arrObj.filter(obj => {
  return obj.types.indexOf("abcde") > -1;
});
console.timeEnd("filterIndexOf");

这个想法是检查哪种方法更快且可扩展,我在这个例子中发现 for inside for 是解决这个问题的最快算法(在 node 和 chrome 中,而不是 firefox)。所以问题是:什么时候 filter() 或 map() 比 for 有更好的性能?

【问题讨论】:

  • jsperf.com 运行测试
  • arrObj[x].types,类型从何而来?
  • So the question is: When filter() or map() have better performance than for? 答案是 - 如果您遇到性能瓶颈的情况,您可以运行性能测试。
  • 为了什么?在绝大多数情况下,性能并不重要。如果您要检查一个包含 10 个元素的数组并且只执行一次,那么它是您应用程序中如此微不足道的一部分,以至于过早地进行微优化将不会产生任何结果。在某些情况下,JS 引擎会为您优化代码,因此甚至不可能有一个“通用”的解决方案在 100% 的时间都以相同的方式工作。此外,JS 引擎会因环境而异,甚至会因版本而异。如果 Chrome 明天优化他们的.map,你的“通用”解决方案就会改变。停止过早优化。
  • 在查看了您的测试用例之后,如果您知道自己在尝试做什么,将会有所帮助。 .map 测试实际上非常糟糕,因为你在滥用.map。我只是将其更改为.forEach,并且我将执行时间减少了大约 75%(在很少的测试中,所以我可能会关闭)~200ms -> 50ms。你没有用.map 做任何你不能用.forEach 做的事情,事实上,你正在无缘无故地创建一个全新的数组,这就是执行时间膨胀的原因。

标签: javascript


【解决方案1】:

可能 filer 是最好的,您的循环执行良好,因为您最终得到的是空数组,但如果不是这种情况,您的循环正在执行推送操作。

包含的性能可能比 indexOf 稍好,但并不明显。

在 cmets 中,您注意到您想要一种通用的过滤方法,使用 getter 和比较器是非常通用的full example here

const elementsInObject = 5000000;

//random number between 1 and 11
function randomNum() {
  return Math.floor(Math.random()*100)+1
}

console.time("createObjectArray");
var arrObj = [...new Array(elementsInObject)].map(()=>({
  prop3: [randomNum(),randomNum()]
}));
console.timeEnd("createObjectArray");

const filterFn = getter => comparer => o =>
  comparer(getter(o));

//get a property
const getProp3 = o => o.prop3;
//you can write getB I'm sure

// compare contains
const contains = needle => haystack =>
  haystack.includes(needle);

// compare indexOf
const indexOf = needle => haystack =>
  haystack.indexOf(needle)>-1;

const filterProp3 = filterFn(getProp3);

console.time("filterContains");
arrObj.filter(
  filterProp3(contains(1))
);
console.timeEnd("filterContains");

console.time("filterIndexOf");
arrObj.filter(
  filterProp3(indexOf(1))
);
console.timeEnd("filterIndexOf");
console.log(
  "Items found:",
  arrObj.filter(
    filterProp3(contains(1))
  ).length
);

【讨论】:

  • 只是为了强化我在 cmets 中所说的,不同的引擎可以以不同的方式处理事情。在 Firefox 中,contains 的时间约为 12 毫秒,indexOf 的时间约为 10 毫秒。几乎没有什么值得的区别,但后者更快。在 Chrome 中,contains 在 ~37-40 毫秒内完成,indexOf 在 ~50 毫秒内完成。
  • @vlaz 我有时会比 indexOf 慢一点,但差异可以忽略不计,大多数时候它的包含速度更快。如果这必须在浏览器中运行,并且您有很多项目,则必须在 Web Worker 中运行它。
猜你喜欢
  • 1970-01-01
  • 2020-01-03
  • 2014-10-14
  • 1970-01-01
  • 2010-09-11
  • 2013-09-27
  • 2017-01-28
  • 2016-02-18
  • 1970-01-01
相关资源
最近更新 更多