web-dev-qa-db-ja.com

JavaScriptで配列をループする最も速い方法は何ですか?

私は本からこのようなループを書くべきであることを学びました:

for(var i=0, len=arr.length; i < len; i++){
    // blah blah
}

そのため、arr.lengthは毎回計算されません。

他の人たちは、コンパイラがこれに対していくらかの最適化をすると言うので、あなたはただ書くことができます:

for(var i=0; i < arr.length; i++){
    // blah blah
}

どちらが実際に最良の方法であるかを知りたいだけですか?

221
wong2

最近のほとんどのブラウザでこのテストを実行した後...

http://jsben.ch/y3SpC

現在、ループの最速形式(そして私の考えでは最も構文的に明白なもの)。

長さキャッシングを持つループの標準

for (var i = 0, len = myArray.length; i < len; i++) {

}

これは間違いなくJavaScriptエンジン開発者に拍手を送るケースです。実行時間は、明瞭度賢さではないに最適化する必要があります。

309
jondavidjohn

JavaScriptの配列をループ処理する最も速い方法は次のとおりです。

var len = arr.length;
while (len--) {
    // blah blah
}

完全な比較については、 http://blogs.Oracle.com/greimer/entry/best_way_to_code_a を参照してください。

82
gnur

2016年6月現在、最新のChrome(2016年5月のブラウザ市場の71%でテストが行​​われています):

  • 最速のループはforループです、キャッシュ長の有無にかかわらず、本当に似たようなパフォーマンスを実現します。 (キャッシュされた長さのforループは、キャッシュされていないものよりも良い結果をもたらすことがありましたが、その差はほとんど無視できるため、エンジンは標準を優先するように既に最適化されています。
  • 減少を伴うwhileループは、forループよりも約1.5倍遅くなりました。
  • (標準のforEachのように)コールバック関数を使用したループは、forループよりも約10倍遅くなりました。

このスレッドは古すぎるので、長さをキャッシュする必要があると考えるか、パフォーマンスを向上させるためにデクリメント付きの逆方向トラバースを使用する必要があると考えるのは誤解を招くようなプログラマーです。したがって、私はお勧めします:

  • あなたのアプリがたくさんの項目を繰り返し処理するか、あなたのループコードが頻繁に使われる関数の中にあるならば、単純なforループが答えです:

    for (var i = 0; i < arr.length; i++) {
      // Do stuff with arr[i] or i
    }
    
  • あなたのアプリが実際にたくさんのアイテムを反復処理しない場合、あるいはあちこちで小さな反復処理を実行する必要がある場合は、標準のforEachコールバック、または選択したJSライブラリからの同様の関数を使用する方が理解しやすく、エラーを起こしにくいかもしれません。インデックス変数のスコープは閉じているので、角かっこを使用する必要はありません。配列の値に直接アクセスします。

    arr.forEach(function(value, index) {
      // Do stuff with value or index
    });
    
  • 何十億もの行を繰り返し処理している間に実際に数ミリ秒をスクラッチする必要があり、配列の長さがプロセスを通じて変わらない場合は、forループで長さをキャッシュすることを検討してください。今日ではこれは本当に必要ではないと思いますが。

    for (var i = 0, len = arr.length; i < len; i++) {
      // Do stuff with arr[i]
    }
    
33
CGodo

順序が重要ではない場合は、私はこのスタイルが好きです:

for(var i = array.length; i--; )

それは長さをキャッシュし、書くのはずっと短いです。しかし、それは逆の順序で配列を繰り返します。

28
Felix Kling

たった2018なので更新はいいかもしれません...

そして私は本当に受け入れられた答えに同意しない必要があります。ブラウザによって異なります。いくつかはforEachをより速く、いくつかのfor-loopを、そしていくつかのwhileはすべてのメソッドのベンチマークです http://jsben.ch/mW36e

arr.forEach( a => {
  // ...
}

for(a = 0; ... )のようにfor-loopがたくさんあるのを見ることができるので、 'var'変数がないとグローバルに定義され、これはスピードに劇的に影響するので、遅くなるでしょう。

var arr = arr = new Array(11111111).fill(255);
var benches =     
[ [ "empty", () => {
  for(var a = 0, l = arr.length; a < l; a++);
}]
, ["for-loop", () => {
  for(var a = 0, l = arr.length; a < l; ++a)
    var b = arr[a] + 1;
}]
, ["for-loop++", () => {
  for(var a = 0, l = arr.length; a < l; a++)
    var b = arr[a] + 1;
}]
, ["for-loop - arr.length", () => {
  for(var a = 0; a < arr.length; ++a )
    var b = arr[a] + 1;
}]
, ["reverse for-loop", () => {
  for(var a = arr.length - 1; a >= 0; --a )
    var b = arr[a] + 1;
}]
,["while-loop", () => {
  var a = 0, l = arr.length;
  while( a < l ) {
    var b = arr[a] + 1;
    ++a;
  }
}]
, ["reverse-do-while-loop", () => {
  var a = arr.length - 1; // CAREFUL
  do {
    var b = arr[a] + 1;
  } while(a--);   
}]
, ["forEach", () => {
  arr.forEach( a => {
    var b = a + 1;
  });
}]
, ["for..in (only 3.3%)", () => {
  var ar = arr.slice(0,arr.length/33);
  for( const a in ar ) {
    var b = a + 1;
  }
}]
, ["Duff's device", () => {
  var i = 0;
  var r = arr.length % 8;
  var n = (arr.length - r) / 8;
  if (r > 0) do {
      var b = arr[i++] + 1;
    }
    while (--r);
  if (n > 0) do {
      var b = arr[i] + 1;
      var c = arr[i+1] + 1;
      var d = arr[i+2] + 1;
      var e = arr[i+3] + 1;
      var f = arr[i+4] + 1;
      var g = arr[i+5] + 1;
      var h = arr[i+6] + 1;
      var k = arr[i+7] + 1;
      i = --n >>> 3;
    }
    while (n);
}]
, ["Duff's device negative", () => {
  var r = arr.length % 8;
  var n = (arr.length-r) / 8; ///Math.floor(arr.length / 8);
  var i = arr.length ; // -1;

  while(r){
    var b = arr[--i] + 1;
    --r;
  }

  while(n){
      var b = arr[i] + 1;
      var c = arr[i-1] + 1;
      var d = arr[i-2] + 1;
      var e = arr[i-3] + 1;
      var f = arr[i-4] + 1;
      var g = arr[i-5] + 1;
      var h = arr[i-6] + 1;
      var j = arr[i-7] + 1;
      i = --n >>> 3;
  }
}]];
function bench(title, f) {
  var t0 = performance.now();
  var res = f();
  return performance.now() - t0; // console.log(`${title} took ${t1-t0} msec`);
}
var globalVarTime = bench( "for-loop without 'var'", () => {
  // Here if you forget to put 'var' so variables'll be global
  for(a = 0, l = arr.length; a < l; ++a)
     var b = arr[a] + 1;
});
var times = benches.map( function(a) {
                      arr = new Array(11111111).fill(255);
                      return [a[0], bench(...a)]
                     }).sort( (a,b) => a[1]-b[1] );
var max = times[times.length-1][1];
times = times.map( a => {a[2] = (a[1]/max)*100; return a; } );
var template = (title, time, n) =>
  `<div>` +
    `<span>${title} &nbsp;</span>` +
    `<span style="width:${3+n/2}%">&nbsp;${Number(time.toFixed(3))}msec</span>` +
  `</div>`;

var strRes = times.map( t => template(...t) ).join("\n") + 
            `<br><br>for-loop without 'var' ${globalVarTime} msec.`;
var $container = document.getElementById("container");
$container.innerHTML = strRes;
body { color:#fff; background:#333; font-family:helvetica; }
body > div > div {  clear:both   }
body > div > div > span {
  float:left;
  width:43%;
  margin:3px 0;
  text-align:right;
}
body > div > div > span:nth-child(2) {
  text-align:left;
  background:darkorange;
  animation:showup .37s .111s;
  -webkit-animation:showup .37s .111s;
}
@keyframes showup { from { width:0; } }
@-webkit-keyframes showup { from { width:0; } }
<div id="container"> </div>
22
nullqube

2014 Whileが帰ってきました

論理的に考えてください。

これを見てください

for( var index = 0 , length = array.length ; index < length ; index++ ) {

 //do stuff

}
  1. 少なくとも2つの変数(インデックス、長さ)を作成する必要があります
  2. インデックスが長さより小さいかどうかを確認する必要があります
  3. インデックスを大きくする必要があります
  4. forループには3つのパラメータがあります

これがなぜこれより速いのかを教えてください。

var length = array.length;

while( --length ) { //or length--

 //do stuff

}
  1. 1変数
  2. チェックなし
  3. 指数が減少する(マシンはそれを好む)
  4. whileには1つのパラメータしかありません

Chrome 28でforループの実行時間が早いことが判明したとき、私はまったく混乱しました。これはある種のベンベンを持っている必要があります

「えーと、誰もがforループを使っています。for chromeを開発する際には、そのことに集中しましょう。」

しかし今、2014年にはwhileループはクロムに戻りました。 2倍速く、他のブラウザや古いブラウザでは常に速いです。

最近私はいくつかの新しいテストを行いました。実社会の環境では、これらの短いコードは意味がなく、jsperfはwhileループを正しく実行できません。これには、array.lengthを再作成する必要があるため時間もかかります。

あなたはjsperf上でwhileループの実際の速度を得ることができません。

あなたはあなた自身のカスタム関数を作成し、window.performance.now()でそれをチェックする必要があります

ええと…whileループを単純に速くする方法はありません。

本当の問題は、実際にはDOMの操作/レンダリング時間/描画時間です。

例えば、私は座標と衝突を計算する必要があるキャンバスシーンを持っています...これは10-200マイクロ秒の間に行われます(ミリ秒ではありません)。 DOMと同じように、実際にすべてをレンダリングするのに数ミリ秒かかります。

BUT

場合によっては、for loopを使用したもう1つの優れた方法があります。

for(
 var i = array.length ;
 i > 0 ;
 arrayCopy[ --i ] = array[ i ] // doing stuff
);

パラメータの設定に注意してください。

  1. Whileループと同じで、変数を1つだけ使用しています。
  2. インデックスが0より大きいかどうかを確認する必要があります。
  3. ご覧のとおり、このアプローチは、3番目のパラメータ内で処理を行い、配列内で直接減少するため、全員が使用する通常のforループとは異なります。

これは、マシンが - のようであることを確認するということを言いました

もう少し短くして無駄なものを取り除き、同じスタイルを使ってこれを書いたと書いています。

for(
 var i = array.length ;
 i-- ;
 arrayCopy[ i ] = array[ i ] // doing stuff
);

たとえもっと短くてもiを使うともう一度遅くなります。前のforループとwhileループよりも1/5遅くなります。

注:;がないfor loooの後は、{}が非常に重要です。

Jsperfはスクリプトをテストする最良の方法ではないと私が言ったとしても、この2つのループをここに追加しました。

http://jsperf.com/caching-array-length/4

そしてここにJavaScriptのパフォーマンスについての別の答えがあります

https://stackoverflow.com/a/21353032/24507

この答えはjavascriptを書くのに効果的な方法を示すことです。あなたがそれを読むことができないのであれば、尋ねるとあなたは答えを得るか、またはJavaScriptについての本を読む http://www.ecma-international.org/ecma-262/5.1/

19
cocco

http://jsperf.com/caching-array-length/6

私が(古いものを再利用することによって)準備したtestの最新版は、一つのことを示しています。

キャッシングの長さはそれほど重要ではありませんが、害はありません

私のDebian Squeeze 64ビット版では、Chrome、Opera、Firefoxの4つのスニペット(チャートでは3、5、7、10)目で、上のリンクを(最初に開いたタブで)最初に実行するたびに最良の結果が得られます( 私のデスクトップハードウェア )。後続の実行ではまったく異なる結果が得られます。

パフォーマンス面での結論は簡単です。

  • forループ(進む)に進み、!==の代わりに<を使用してテストします。
  • 後でこの配列を再利用する必要がない場合は、デクリメントされた長さと破壊的なshift()配列に対するwhileループも効率的です。

tl; dr

パターンの下の今日(2011.10)は最速のもののようです。

for (var i = 0, len = arr.length; i !== len; i++) {
    ...
}

arr.lengthをキャッシュすることはここでは重要ではないので、i !== arr.lengthをテストするだけでパフォーマンスは低下しませんが、コードが短くなります。


PS:shift()のスニペットでは、0番目の要素にアクセスする代わりにその結果を使用できることはわかっていますが、以前のリビジョン(ループ中に間違っていた)を再利用した後.

11
przemoc

純粋なパフォーマンスのように「最高」?またはパフォーマンスおよびの可読性?

純粋なパフォーマンス「最高の」これは、キャッシュと++プレフィックス演算子を使用する(私のデータ: http://jsperf.com/caching-array-length/189

for (var i = 0, len = myArray.length; i < len; ++i) {
  // blah blah
}

私は、キャッシュレスforループが実行時間とプログラマーの読み取り時間の間で最良のバランスであると主張するでしょう。 C/C++/Javaで始まったすべてのプログラマーはこれを通読しなければならないmsを無駄にしません

for(var i=0; i < arr.length; i++){
  // blah blah
}
8
valbaca

**ループの内側に配列の長さをキャッシュすると、数秒の時間がかかります。配列内の項目数が多い場合は、配列内の項目に依存します。

**

sArr; //Array[158];

for(var i = 0 ; i <sArr.length ; i++) {
 callArray(sArr[i]); //function call
}

***end: 6.875ms***

**

**

sArr; //Array[158];
for(var i = 0,len = sArr.length ; i < len ; i++) {
  callArray(sArr[i]); //function call
}

***end: 1.354ms***

**

7

これ 最速の方法に見える はるかに...

var el;
while (el = arr.shift()) {
  el *= 2;
}

これが配列を消費し、それを食べ、そして何も残さないことを考慮に入れてください...

6
Sergio

2017年です2017

私はいくつかテストをしました。

https://jsperf.com/fastest-way-to-iterate-through-an-array/

whileメソッドがChromeで最も速いようです。

Firefoxの左の減少(--i)は他のもの(++ii--i++)よりはるかに速いように見えます。

このアプローチは平均して断食です。しかし、それは逆の順序で配列を繰り返します。

let i = array.length;
while (--i >= 0) {
    doSomething(array[i]);
}

順方向が重要な場合は、この方法を使用してください。

let ii = array.length;
let i = 0;
while (i < ii) {
    doSomething(array[i]);
    ++i;
}
4
SeregPie

私はいつも最初のスタイルで書いています。

たとえコンパイラがそれを配列用に最適化するのに十分スマートであったとしても、それでもここでDOMNodeListまたは計算された長さを持つ複雑なオブジェクトを使用しているならばそれでもスマートですか?

配列に関する問題はわかっていますが、すべてのループを1つのスタイルで記述するのは得策です。

2
Olegas
var arr = []; // The array
var i = 0;
while (i < arr.length) {
    // Do something with arr[i]
    i++;
}

i ++は++ i、 - i、i--より速いです

また、最後にiにアクセスする必要があるときにarr [i ++]を実行して最後の行を保存することもできます(ただし、これはデバッグが難しい場合があります)。

あなたはここでそれをテストすることができます(他のループテストで): http://jsperf.com/for-vs-whilepop/5

1
Forestrf

私は巨大な配列を繰り返すために他の方法を試してみましたが、配列の長さを半分にしてから両方の半分を単一のループで繰り返す方が速いことがわかりました。このパフォーマンスの違いは、処理中に見られます巨大な配列

var firstHalfLen =0;
var secondHalfLen = 0;
var count2=0;
var searchterm = "face";
var halfLen = arrayLength/2;
if(arrayLength%2==halfLen)
{
   firstHalfLen = Math.ceil(halfLen);
   secondHalfLen=Math.floor(halfLen);
}
else
{
   firstHalfLen=halfLen;
   secondHalfLen=halfLen;
}
for(var firstHalfCOunter=0,secondHalfCounter = arrayLength-secondHalfLen;
    firstHalfCOunter < firstHalfLen;
    firstHalfCOunter++)
{
  if(mainArray[firstHalfCOunter].search(new RegExp(searchterm, "i"))> -1)
  {
    count2+=1;
  }
  if(secondHalfCounter < arrayLength)
  {
    if(mainArray[secondHalfCounter].search(new RegExp(searchterm, "i"))> -1)
    {
        count2+=1;
    }
    secondHalfCounter++; 
  }
}

キャッシュされた長さのfor-loop VSと上記の方法のパフォーマンス比較(timer.jsを使用)。

http://jsfiddle.net/tejzpr/bbLgzxgo/

0
tejzpr

私が知っている最もエレガントな解決策は地図を使うことです。

var arr = [1,2,3];
arr.map(function(input){console.log(input);});
0
Dan

Whileループはforループより少し速いです。

var len = arr.length;
while (len--) {
    // blah blah
}

代わりにwhileループを使う

0
Azhar Zafar

基本的なwhileループが最速です。 jsperf.comは、この種の概念をテストするための優れたサンドボックスです。

https://jsperf.com/fastest-array-loops-in-javascript/24

0
Devappended

別のjsperf.comテスト: http://jsperf.com/while-reverse-vs-for-cached-length

逆のwhileループが最も速いようです。唯一の問題は、while(--i)が0で停止することです。それでは、ループ内でarray [0]にアクセスするにはどうすればよいですか。

2017年9月現在 これらのjsperfテスト は、Chrome 60上で最もパフォーマンスが良いとされる次のパターンを示しています。

function foo(x) {
 x;
};
arr.forEach(foo);

誰でも再現することができますか?

0
John Vandivier

これを試して:

var myarray =[],
i = myarray.lenght;
while(i--){
// do somthing
}
0
li bing zhao

2019年以降、WebWorkerはより大きな人気を博しています。大規模なデータセットでは、マルチコアプロセッサをフルに活用することで、WebWorkerを使用してはるかに高速に処理できます。

また、 Parallel.js があるので、WebWorkerはデータ処理にはるかに使いやすくなります。

0
Stackia