最快的JavaScript求和


70

用JavaScript总结数组的最快方法是什么?快速搜索介绍了几种不同的方法,但如果可能的话,我想使用本机解决方案。这将在SpiderMonkey下运行。

我一直在想:

var count = 0;
for(var i = 0; i < array.length; i++)
{
    count = count + array[i];
}

我敢肯定有比直接迭代更好的方法。


3
测试!!!如果您需要了解最快的方法是什么,请尝试几种方法并测量结果。
CaffGeek 2010年

4
@Chad:显然,但是我不是在“开箱即用”的日子里。
乔什(Josh K)2010年

Answers:


139

您应该可以使用reduce

var sum = array.reduce(function(pv, cv) { return pv + cv; }, 0);

资源

借助ES6中引入的箭头功能,它甚至更加简单:

sum = array.reduce((pv, cv) => pv + cv, 0);

1
根据链接的文档,“ [reduce]可能并非在所有浏览器中都可用”
Ryan Kinal 2010年

@Tim:同样,它是在SpiderMonkey下运行的,而不是任何特定的浏览器。
乔什(Josh K)2010年

啊,对不起,我错过了。在这种情况下,这就是答案。
蒂姆·唐

11
我以某种方式怀疑它,即使它reduce是本机函数。在JS中面向性能的代码中,避免函数调用几乎总是更快,
MooGoo 2010年

4
@BrunoLM这不再是真的!
Cirelli94 '18

32

改进措施


您的循环结构可以变得更快:


   var count = 0;
   for(var i=0, n=array.length; i < n; i++) 
   { 
      count += array[i]; 
   }

这将检索array.length一次,而不是每次迭代。通过缓存值进行优化。


如果您真的想加快速度:


   var count=0;
   for (var i=array.length; i--;) {
     count+=array[i];
   }

这等效于一会儿反向循环。它缓存该值并将其与0进行比较,从而加快了迭代速度。

有关更完整的比较列表,请参见我的JSFiddle
注意: array.reduce在那里很可怕,但是在Firebug控制台中它是最快的。


比较结构

我启动了一个 JSPerf进行数组求和。它是快速构建的,不能保证其完整性或准确性,但这就是编辑的目的:)


3
您的for循环几乎相等。我进行了测试,有时增加的速度比减少的速度快。而且Array.reduce非常慢。jsperf.com/array-reduce-vs-foreach/2
BrunoLM

@BrunoLM:是的,这是3年前的老答案。我想不久之后,便有了一些新的JS引擎,并对现有的引擎进行了调整,以便向前循环比反向循环更快。这表明了为什么微优化通常是不明智的。我将继续进行测试和基准测试-如果您没有本地套件,jsperf是一个不错的网站。
vol7ron

链接的JS小提琴在“ For循环:长度缓存(反向)”中有错误。总是将索引0处的元素加到总和上。for (var i = 0, n = array.length; n > i; n--) { sum += array[i]; }应该更改为for (var i = 0, n = array.length-1; n >= i; n--) { sum += array[n]; }。这使它与其他缓存循环处于同一状况。
Lukas_Skywalker

20

在寻找对数组求和的最佳方法时,我编写了一个性能测试。

在Chrome浏览器中,“ reduce”似乎非常优越

我希望这有帮助

// Performance test, sum of an array
  var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  var result = 0;
// Eval
  console.time("eval");
  for(var i = 0; i < 10000; i++) eval("result = (" + array.join("+") + ")");
  console.timeEnd("eval");
// Loop
  console.time("loop");
  for(var i = 0; i < 10000; i++){
    result = 0;
    for(var j = 0; j < array.length; j++){
      result += parseInt(array[j]);
    }
  }
  console.timeEnd("loop");
// Reduce
  console.time("reduce");
  for(var i = 0; i < 10000; i++) result = array.reduce(function(pv, cv) { return pv + parseInt(cv); }, 0);
  console.timeEnd("reduce");
// While
  console.time("while");
  for(var i = 0; i < 10000; i++){
    j = array.length;
    result = 0;
    while(j--) result += array[i];
  }
  console.timeEnd("while");

评估:5233.000ms

循环:255.000ms

减少:70.000ms

同时:214.000ms


1
谢谢,为什么parseInt在reduce函数中需要一个a ?我尝试过,我的代码中也需要它。
艾伦·王

嗯,不确定,请在没有parseInt的情况下尝试一下。.我4年前写了这篇文章:D
Inkh Su Tesou


7

根据此测试,最快的循环是反向的while循环

var i = arr.length; while (i--) { }

因此,此代码可能是最快的

Array.prototype.sum = function () {
    var total = 0;
    var i = this.length; 

    while (i--) {
        total += this[i];
    }

    return total;
}

Array.prototype.sum 向数组类添加一个sum方法...您可以轻松地使其成为一个辅助函数。


我的反向略快一点,而不是更多次。
vol7ron

@ vol7ron,非常,非常,非常非常地,我们正在谈论
〜1ms

:)是的,不是每次都这样。不过,for(var i=0,n=a.length;i<n;i++){}由于开始/停止控制结构,我更可能使用它。
vol7ron

哪里arr来从sum方法?应该arr真的this吗?
克里斯·法默

看来,评估while条件到布尔值可以使其变得更快jsperf.com/while-bool-vs-while
BrunoLM

2

对于您的特定情况,只需使用reduceArrays方法:

var sumArray = function() {
    // Use one adding function rather than create a new one each
    // time sumArray is called
    function add(a, b) {
        return a + b;
    }

    return function(arr) {
        return arr.reduce(add);
    };
}();

alert( sumArray([2, 3, 4]) );

1

基于此测试(for-vs-forEach-vs-reduce)此(循环)

我可以这样说:

1#最快:for循环

var total = 0;

for (var i = 0, n = array.length; i < n; ++i)
{
    total += array[i];
}

2#聚合

对于您而言,您将不需要此功能,但是它增加了很多灵活性。

Array.prototype.Aggregate = function(fn) {
    var current
        , length = this.length;

    if (length == 0) throw "Reduce of empty array with no initial value";

    current = this[0];

    for (var i = 1; i < length; ++i)
    {
        current = fn(current, this[i]);
    }

    return current;
};

用法:

var total = array.Aggregate(function(a,b){ return a + b });

不确定的方法

然后出现了forEachreduce它们几乎具有相同的性能,并且随浏览器的不同而有所差异,但是它们的性能仍然最差。


1

我尝试使用performance.now()分析不同类型的循环的性能。我采用了一个非常大的数组,并发现了数组中所有元素的总和。我每次都运行代码三次,发现的forEach减少是一个明显的赢家。

// For循环

let arr = [...Array(100000).keys()]
function addUsingForLoop(ar){
  let sum = 0;
  for(let i = 0; i < ar.length; i++){
    sum += ar[i];
  }
   console.log(`Sum: ${sum}`);
   return sum;
}
let t1 = performance.now();
addUsingForLoop(arr);
let t2 = performance.now();
console.log(`Time Taken ~ ${(t2 - t1)} milliseconds`)

// "Sum: 4999950000"
// "Time Taken ~ 42.17500000959262 milliseconds"
// "Sum: 4999950000"
// "Time Taken ~ 44.41999999107793 milliseconds"
// "Sum: 4999950000"
// "Time Taken ~ 49.845000030472875 milliseconds"

// While循环

let arr = [...Array(100000).keys()]
function addUsingWhileLoop(ar){
let sum = 0;
let index = 0;
while (index < ar.length) {
  sum += ar[index];
  index++;
}
  console.log(`Sum: ${sum}`)
  return sum;
}
let t1 = performance.now();
addUsingWhileLoop(arr);
let t2 = performance.now();
console.log(`Time Taken ~ ${(t2 - t1)} milliseconds`)

// "Sum: 4999950000"
// "Time Taken ~ 44.2499999771826 milliseconds"
// "Sum: 4999950000"
// "Time Taken ~ 44.01999997207895 milliseconds"
// "Sum: 4999950000"
// "Time Taken ~ 41.71000001952052 milliseconds"

//做一会儿

let arr = [...Array(100000).keys()]
function addUsingDoWhileLoop(ar){
let sum = 0;
let index = 0;
do {
   sum += index;
   index++;
} while (index < ar.length);
   console.log(`Sum: ${sum}`);
   return sum;
}
let t1 = performance.now();
addUsingDoWhileLoop(arr);
let t2 = performance.now();
console.log(`Time Taken ~ ${(t2 - t1)} milliseconds`)

// "Sum: 4999950000"
// "Time Taken ~ 43.79500000504777 milliseconds"
// "Sum: 4999950000"
// "Time Taken ~ 43.47500001313165 milliseconds"
// "Sum: 4999950000"
// "Time Taken ~ 47.535000019706786 milliseconds"

//反向循环

let arr = [...Array(100000).keys()]
function addUsingReverseLoop(ar){
   var sum=0;
   for (var i=ar.length; i--;) {
     sum+=arr[i];
   }
   console.log(`Sum: ${sum}`);
   return sum;
}
let t1 = performance.now();
addUsingReverseLoop(arr);
let t2 = performance.now();
console.log(`Time Taken ~ ${(t2 - t1)} milliseconds`)

// "Sum: 4999950000"
// "Time Taken ~ 46.199999982491136 milliseconds"
// "Sum: 4999950000"
// "Time Taken ~ 44.96500000823289 milliseconds"
// "Sum: 4999950000"
// "Time Taken ~ 43.880000011995435 milliseconds"

//反转while循环

let arr = [...Array(100000).keys()]
function addUsingReverseWhileLoop(ar){
    var sum = 0;
    var i = ar.length; 
    while (i--) {
        sum += ar[i];
    }
    console.log(`Sum: ${sum}`);
    return sum;
}
var t1 = performance.now();
addUsingReverseWhileLoop(arr);
var t2 = performance.now();
console.log(`Time Taken ~ ${(t2 - t1)} milliseconds`)

// "Sum: 4999950000"
// "Time Taken ~ 46.26999999163672 milliseconds"
// "Sum: 4999950000"
// "Time Taken ~ 42.97000000951812 milliseconds"
// "Sum: 4999950000"
// "Time Taken ~ 44.31500000646338 milliseconds"

// 降低

let arr = [...Array(100000).keys()]
let t1 = performance.now();
sum = arr.reduce((pv, cv) => pv + cv, 0);
console.log(`Sum: ${sum}`)
let t2 = performance.now();
console.log(`Time Taken ~ ${(t2 - t1)} milliseconds`)

// "Sum: 4999950000"
// "Time Taken ~ 4.654999997001141 milliseconds"
// "Sum: 4999950000"
// "Time Taken ~ 5.040000018198043 milliseconds"
// "Sum: 4999950000"
// "Time Taken ~ 4.835000028833747 milliseconds"

// forEach

let arr = [...Array(100000).keys()]
function addUsingForEach(ar){
  let sum = 0;
  ar.forEach(item => {
    sum += item;
  })
    console.log(`Sum: ${sum}`);
    return sum
}
let t1 = performance.now();
addUsingForEach(arr)
let t2 = performance.now();
console.log(`Time Taken ~ ${(t2 - t1)} milliseconds`)

// "Sum: 4999950000"
// "Time Taken ~ 5.315000016707927 milliseconds"
// "Sum: 4999950000"
// "Time Taken ~ 5.869999993592501 mienter code herelliseconds"
// "Sum: 4999950000"
// "Time Taken ~ 5.405000003520399 milliseconds"

+1,但是最好有一个jsperf来验证这一点。我相信这些数字将根据使用的js引擎而有所不同。
FullStackForger

0

最简单,最快,更可重用和灵活的一种是:

Array.prototype.sum = function () {
    for(var total = 0,l=this.length;l--;total+=this[l]); return total;
}

// usage
var array = [1,2,3,4,5,6,7,8,9,10];
array.sum()

5
大声笑!这既不比reduce更简单,也不快,也不具有重用性和灵活性!
Corey Alix

这确实是最快的(请参阅jsben.ch/0Qa3G),并且可以通过另一种有趣的方式来完成class UintArray extends Uint8Array { sum () { FUNCTION_CODE_HERE } }
Noah,

更改数组原型会中断..in循环!
NVRM

0

两个末端相加怎么办?这样可以将时间缩短一半。像这样:

1、2、3、4、5、6、7、8;总和= 0

2、3、4、5、6、7;总和= 10

3,4,5,6; 总和= 19

4、5;总和= 28

总和= 37

一种算法可以是:

function sum_array(arr){
    let sum = 0,
        length = arr.length,
        half = Math.floor(length/2)

    for (i = 0; i < half; i++) {
        sum += arr[i] + arr[length - 1 - i]
    }
    if (length%2){
        sum += arr[half]
    }
    return sum
}

当我使用进行测试时,它的执行速度更快performance.now()。我认为这是一种更好的方法。你们有什么感想?


1
从技术上讲,Big-O-表示法O(n/2)等于O(n)。为什么?因为您要估算的不是给定输入的速度,而是速度随着输入的变化而变化。如果将O(n)函数的输入加倍,则需要花费两倍的时间。如果将O(n / 2)函数的输入加倍,则需要花费两倍的时间。如果将输入倍增为O(n²),则需要四倍的时间。
t.animal
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.