วิธีที่เร็วที่สุดในการวนซ้ำอาร์เรย์ใน JavaScript คืออะไร


249

ฉันเรียนรู้จากหนังสือที่คุณควรเขียนให้เหมือนกัน:

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

ดังนั้นarr.lengthจะไม่ถูกคำนวณในแต่ละครั้ง

คนอื่นพูดว่าคอมไพเลอร์จะทำการปรับให้เหมาะสมเพื่อให้คุณสามารถเขียน:

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

ฉันแค่อยากรู้ว่าวิธีไหนดีที่สุดในการฝึก?


1
ควรค่าแก่การดูเมื่อต้องรับมือกับอาร์เรย์ลูป: jsperf.com/array-loop-var-caching
cloakedninjas

@ wong2 เกณฑ์มาตรฐาน TthisจากBrowserdietมีตัวเลือกที่ครบถ้วนกว่า
Domi


ปรับปรุงใน jsben ก่อนหน้านี้: jsben.ch/#/R6LbS
Corbfon

เราสามารถแนะนำfor ... ofวงรอบการแข่งขันนี้ได้หรือไม่? ดูเหมือนว่าไวยากรณ์ง่ายขึ้นสำหรับลูปโดยไม่ต้องแคชและฉันต้องการทราบว่าฉันควรเปลี่ยนไปใช้สำหรับลูปหรือไม่
programmerRaj

คำตอบ:


339

หลังจากทำการทดสอบนี้กับเบราว์เซอร์ที่ทันสมัยที่สุด ...

http://jsben.ch/dyM52

ขณะนี้รูปแบบของวงที่เร็วที่สุด (และในความคิดของฉันชัดเจนที่สุด syntactically)

มาตรฐานสำหรับลูปที่มีการแคชความยาว

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

}

ฉันจะบอกว่านี่เป็นกรณีที่ฉันปรบมือให้นักพัฒนาเครื่องมือ JavaScript เวลาทำงานควรได้รับการปรับให้เหมาะสมกับความคมชัด , ไม่ฉลาด


6
ที่น่าสนใจใน IE9 นี้เร็วกว่า: สำหรับ (var i = 0, len = myArray.length; i <len; ++ i) {} // ส่วนนำหน้า incr แทน postfix
Christopher Bennage

4
ดูชอบประกอบคำนำหน้ามากกว่า postfixด้วยเหตุผลอื่น ๆ ++iกับการใช้งาน
Bennett McElwee

4
ฉันทดสอบโดยใช้ตัวดำเนินการคำนำหน้าตามที่ @BennettMcElwee แนะนำและมันทำงานได้เร็วขึ้นเล็กน้อย: for(var i=0, len=myArray.length; i<len; ++i) ตรวจสอบjsperf.com/caching-array-length/84
victmo

21
คุณต้องระวังการใช้วงนี้ ฉันเริ่มใช้มันและมีปัญหาในการติดตามข้อผิดพลาดอย่างหนัก หากคุณรังสอง loops เช่นนี้jsfiddle.net/KQwmL/1 คุณต้องระวังในการตั้งชื่อ var len ให้แตกต่างกันในสองลูปไม่เช่นนั้นลูปที่สองจะเขียนทับเลนแรก
Rui Marques

6
@WillshawMedia คุณสามารถประกาศหลายตัวแปรได้ด้วยvarคำสั่งเดียว การเขียนนั้นlenถูกกำหนดขอบเขตตามที่คุณแนะนำ
jondavidjohn

90

วิธีที่เร็วที่สุดในการวนซ้ำผ่านอาร์เรย์ของ javascript คือ:

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

ดูhttp://blogs.oracle.com/greimer/entry/best_way_to_code_aสำหรับการเปรียบเทียบแบบเต็ม


1
อย่าลืมที่จะใช้var(มิฉะนั้นlenจะกลายเป็นตัวแปรทั่วโลก) และดูjsperf.com/loopsสำหรับการวัดลูปมาตรฐานเพิ่มเติม
Mathias Bynens

22
บล็อกโพสต์คำตอบนี้อิงจากตอนนี้เกือบ 4 ปีและมีการเปลี่ยนแปลงมากมายในเครื่องยนต์ js ในเวลานั้นโปรดดูคำตอบของฉันด้านล่างสำหรับการเปรียบเทียบที่อัปเดต
jondavidjohn

1
ฉันเห็นด้วยกับ @jondavidjohn ฉันทดสอบโค้ดนี้และดูเหมือนว่าจะมีประสิทธิภาพน้อยกว่า ... ตรวจสอบ jsperf.com/caching-array-length/84
victmo

คำตอบข้างต้นเกือบเป็นสากล (ในเบราว์เซอร์) ช้ากว่าแบบ for-loop มาก ดูลิงก์ JSPerf ในคำตอบที่ยอมรับได้ มันเป็นความอัปยศที่ยิ่งใหญ่เพราะมันเป็น IMO ที่อ่านง่ายมาก
Letharion

3
ฉันคาดเดา @jondavidjohn ว่าสิ่งที่คุณหมายถึงโดย 'คำตอบของฉันด้านล่าง' คือ 'คำตอบของฉันด้านบน' lol
Shanimal

40

ตั้งแต่เดือนมิถุนายน 2559ทำการทดสอบบางอย่างใน Chrome ล่าสุด (71% ของตลาดเบราว์เซอร์ในเดือนพฤษภาคม 2559 และเพิ่มขึ้น):

  • การวนซ้ำที่เร็วที่สุดนั้นใช้สำหรับการวนซ้ำทั้งที่มีและไม่มีความยาวแคชให้ประสิทธิภาพที่คล้ายกันจริงๆ (สำหรับลูปที่มีความยาวแคชบางครั้งให้ผลลัพธ์ที่ดีกว่าอันที่ไม่มีการแคช แต่ความแตกต่างนั้นเล็กน้อยมากซึ่งหมายความว่าเครื่องยนต์อาจได้รับการปรับให้เหมาะกับมาตรฐานและอาจตรงไปตรงมาที่สุดสำหรับลูปโดยไม่ต้องแคช)
  • ขณะที่ลูปที่มีการลดลงช้ากว่าลูป for ประมาณประมาณ 1.5 เท่า
  • การวนซ้ำที่ใช้ฟังก์ชันการเรียกกลับ (เช่น forEach มาตรฐาน) นั้นช้ากว่าการ for loop ประมาณ 10 เท่า

ฉันเชื่อว่าเธรดนี้เก่าเกินไปและทำให้ผู้เขียนเข้าใจผิดคิดว่าพวกเขาจำเป็นต้องแคชความยาวหรือใช้การย้อนกลับแบบข้ามพร้อมการลดขนาดเพื่อให้ได้ประสิทธิภาพที่ดีขึ้นการเขียนโค้ดที่อ่านง่ายและมีแนวโน้มที่จะเกิดข้อผิดพลาด ดังนั้นฉันแนะนำ:

  • หากแอปของคุณวนซ้ำรายการจำนวนมากหรือรหัสลูปของคุณอยู่ในฟังก์ชันที่ใช้บ่อยคำตอบที่ตรงไปตรงมาสำหรับลูปคือคำตอบ:

    for (var i = 0; i < arr.length; i++) {
      // Do stuff with arr[i] or i
    }
    
  • หากแอปของคุณไม่ซ้ำไปซ้ำมาหลายรายการหรือคุณต้องทำซ้ำขนาดเล็กที่นี่และที่นั่นการใช้การเรียกกลับมาตรฐานแต่ละครั้งหรือฟังก์ชั่นอื่น ๆ ที่คล้ายกันจากไลบรารี JS ที่คุณเลือกอาจเข้าใจง่ายและมีข้อผิดพลาดน้อยลง ขอบเขตตัวแปรดัชนีถูกปิดและคุณไม่จำเป็นต้องใช้วงเล็บเข้าถึงค่าอาร์เรย์โดยตรง:

    arr.forEach(function(value, index) {
      // Do stuff with value or index
    });
    
  • หากคุณต้องการเกาสองสามมิลลิวินาทีในขณะที่วนซ้ำแถวหลายพันล้านแถวและความยาวของอาร์เรย์ของคุณจะไม่เปลี่ยนแปลงไปตามกระบวนการคุณอาจพิจารณาการแคชความยาวในการวนซ้ำ แม้ว่าฉันคิดว่ามันไม่จำเป็นจริงๆในปัจจุบัน:

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

Nope jsbench.github.io/#67b13d4e78cdd0d7a7346410d5becf12แสดงให้เห็นว่าเร็วที่สุดคือ "Reverse loop, การเปรียบเทียบโดยนัย, โค้ดอินไลน์" (105,221 ops / วินาที) ในขณะที่ "Loop, แคช, inlined code" ทำคะแนนได้เพียง 76,635 ops / วินาที (Chrome 38.0.2.11.11 )
Fr0sT

@ Fr0sT เกณฑ์มาตรฐานของคุณเป็นสถานการณ์ที่แตกต่างกันโดยสำรวจอาร์เรย์จากดัชนี 1 ถึง <= ความยาว แน่นอนว่าสิ่งนี้จะนำไปสู่ผลลัพธ์ที่แตกต่าง หากคุณลองใช้อาร์เรย์แบบ zero-based ด้วย <length - ซึ่งดูเหมือนว่าสำหรับฉันแล้วมันเป็นสถานการณ์ปกติ - คุณจะค้นพบว่าผลลัพธ์จะได้รับการปรับปรุงให้ดีขึ้นด้วย "for" loop ปกติ (โดยความยาวแคชจะเร็วขึ้นเล็กน้อย)
CGodo

Kyopaxa เปลี่ยนมาตรฐานเป็น (0 <= i <ความยาว) ผลลัพธ์เหมือนกัน "Reverse loop, การเปรียบเทียบโดยนัย, การเรียกใช้ฟังก์ชัน" ได้คะแนน 365 kops / วินาทีในขณะที่ "Loop, ค่าแคช, รหัสแบบ
อินไลน์

@ Fr0sT หากคุณเปลี่ยน for-loop ที่แคชเป็นศูนย์โดยไม่มีการเปรียบเทียบที่เท่ากันเช่นfor(let i=0, j=array.length; i < j; i++)การส่งต่อสำหรับลูปจะเพิ่มความเร็วขึ้นอย่างมาก ในการทดสอบสองสามครั้งที่ฉันวิ่งมันชนะส่วนใหญ่มันอยู่ในระยะขอบของความผิดพลาดหรือวนรอบย้อนกลับ
Isaac B

1
@IacacB และทั้งหมดขอโทษฉันไม่ได้สังเกตเห็นว่าม้านั่งค่อนข้างไม่ถูกต้อง - ลูปโดยตรงทั้งหมดย้ำ 1..length ขณะย้อนกลับลูปซ้ำย้ำความยาว. 0 (รายการ [ความยาว] ไม่ถูกต้อง) ฉันแก้ไขการทดสอบและตอนนี้พวกเขาแสดงผลลัพธ์ต่อไปนี้: "วนรหัสแบบอินไลน์" 360,616 ops / วินาที± 0.27%, "วนซ้ำ, ค่าแคช, รหัสแบบอินไลน์" 345,786 ops / วินาที± 2.18% (Sic!) "ย้อนกลับวนรอบ การเปรียบเทียบโดยนัย, โค้ดอินไลน์ "322,640 ops / วินาที± 2.90% (!!!) การทดสอบถูกดำเนินการโดย FF51 ม้านั่งใหม่อยู่ที่นี่jsbench.github.io/#6bdfcd2692ba80c16a68c88554281570 ดังนั้นดูเหมือนว่าไม่มีเหตุผลที่จะทำให้ลูปน่าเกลียด
Fr0sT

31

หากคำสั่งซื้อไม่สำคัญฉันชอบสไตล์นี้:

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

มันแคชความยาวและสั้นกว่ามากในการเขียน แต่มันจะวนซ้ำในลำดับที่ตรงกันข้าม


6
คุณเพิ่งจะฆ่ามัน
Vignesh Raja

คุณไม่ต้องการ i> = 0;
MarwaAhmad

3
@MarwaAhmad: เลขที่i--ส่งกลับจำนวนและเมื่อจำนวนเป็น0เงื่อนไขเป็นเพราะfalse Boolean(0) === false
เฟลิกซ์คลิง

31

เป็นเพียงปี 2018 ดังนั้นการอัปเดตอาจทำได้ดี ...

และผมจะต้องไม่เห็นด้วยกับคำตอบที่ได้รับการยอมรับ มันขัดแย้งกับเบราว์เซอร์ที่แตกต่างกัน บางอันทำงานforEachได้เร็วขึ้นบางส่วนfor-loopและwhile ที่นี่เป็นเกณฑ์มาตรฐานสำหรับวิธีการทั้งหมดhttp://jsben.ch/mW36e

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

และเนื่องจากคุณสามารถเห็นจำนวนมากของ for-loop เช่นfor(a = 0; ... )นั้นมีค่าที่จะกล่าวถึงว่าหากไม่มีตัวแปร 'var' จะถูกกำหนดทั่วโลกและสิ่งนี้สามารถส่งผลกระทบต่อความเร็วอย่างมากดังนั้นมันจะช้าลง

อุปกรณ์ของ Duff ทำงานได้เร็วขึ้นในโอเปร่า แต่ไม่ใช่ใน Firefox

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 const..in (only 3.3%)", () => {
  var ar = arr.slice(0,arr.length/33);
  for( const a in ar ) {
    var b = a + 1;
  }
}]
, ["for let..in (only 3.3%)", () => {
  var ar = arr.slice(0,arr.length/33);
  for( let a in ar ) {
    var b = a + 1;
  }
}]
, ["for var..in (only 3.3%)", () => {
  var ar = arr.slice(0,arr.length/33);
  for( var a in ar ) {
    var b = a + 1;
  }
}]
, ["Duff's device", () => {
  var len = arr.length;
  var i, n = len % 8 - 1;

  if (n > 0) {
    do {
      var b = arr[len-n] + 1;
    } while (--n); // n must be greater than 0 here
  }
  n = (len * 0.125) ^ 0;
  if (n > 0) { 
    do {
      i = --n <<3;
      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;
    }
    while (n); // n must be greater than 0 here also
  }
}]];
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>


3
@Maykonn คุณอาจต้องการที่จะพูดว่า "และใช้งานได้ทุกที่ยกเว้น Opera Mini"
dube

3
@Maykonn ซึ่งไม่อยู่ในรายการในมุมมองเริ่มต้นเนื่องจาก 0.18% ของผู้ใช้ทั้งหมดมี IE8 และคุณไม่ควรเสียเวลาลองสนับสนุน ในปี 2018 เป็นม้าที่ตายแล้ว
dube

1
เป็นเรื่องจริงถ้าคุณพิจารณาผู้ใช้ทุกคนทั่วโลก แต่น่าเสียดายที่ในบางส่วนของโลก IE8 นั้นมีความเกี่ยวข้อง
Maykonn

1
หากฉันทำได้ไม่เพียง แต่เบราว์เซอร์ที่แตกต่างกันเท่านั้นที่จะได้ผลลัพธ์ที่แตกต่างกันด้วยวิธีการที่แตกต่างกัน แต่เบราว์เซอร์เดียวกันจะมีผลลัพธ์ที่แตกต่างกันด้วยอินพุตที่แตกต่างกัน Array จำนวนมากเท่านั้นที่จะได้รับการปรับให้เหมาะสมที่สุด
Kaiido

1
@Tahlil ขอบคุณ
nullqube

19

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. ตัวแปรเดียว
  2. ไม่มีการตรวจสอบ
  3. ดัชนีจะลดลง (เครื่องจักรต้องการมากกว่านั้น)
  4. while มีพารามิเตอร์เดียวเท่านั้น

ฉันสับสนอย่างสิ้นเชิงเมื่อ Chrome 28 แสดงให้เห็นว่าการวนซ้ำเร็วกว่าในขณะนั้น นี่ต้องมีอะไรบางอย่าง

"เอ่อทุกคนกำลังใช้ลูปเพื่อให้เรามุ่งเน้นไปที่การพัฒนาสำหรับโครเมี่ยม"

แต่ตอนนี้ในปี 2014 ขณะที่วงกลับมาที่โครเมียม มันเร็วกว่า 2 เท่าบนเบราว์เซอร์อื่น / เก่ากว่ามันเร็วกว่าเสมอ

เมื่อเร็ว ๆ นี้ฉันได้ทำการทดสอบใหม่ ตอนนี้ในโลกแห่งความเป็นจริงการกำหนดรหัสสั้น ๆ เหล่านี้ไม่มีค่าอะไรเลยและ jsperf ไม่สามารถทำงานได้อย่างถูกต้องในขณะที่ลูปเพราะมันจำเป็นต้องสร้าง array.length อีกครั้งซึ่งต้องใช้เวลา

คุณไม่สามารถรับความเร็วจริงของการวนรอบบน jsperf

คุณต้องสร้างฟังก์ชั่นที่กำหนดเองของคุณเองและตรวจสอบด้วย window.performance.now()

และใช่ ... ไม่มีวิธีใดในขณะที่การวนซ้ำนั้นเร็วขึ้น

ปัญหาที่แท้จริงคืออันที่จริงแล้วการจัดการ / การแสดงเวลา / เวลาการวาดหรือคุณต้องการเรียกมันว่า

ตัวอย่างเช่นฉันมีฉากผ้าใบที่ฉันต้องการคำนวณพิกัดและการชน ... ซึ่งจะทำระหว่าง 10-200 MicroSeconds (ไม่ใช่มิลลิวินาที) ใช้เวลาหลายมิลลิวินาทีในการแสดงทุกอย่างเหมือนกันใน DOM

แต่

มีอีกวิธี Super Performant ใช้สำหรับloopในในบางกรณี ... ตัวอย่างเช่นการคัดลอก / โคลนอาร์เรย์

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

สังเกตการตั้งค่าพารามิเตอร์:

  1. เหมือนกับในขณะที่ลูปฉันใช้ตัวแปรเดียวเท่านั้น
  2. ต้องตรวจสอบว่าดัชนีใหญ่กว่า 0 หรือไม่
  3. อย่างที่คุณเห็นว่าวิธีนี้แตกต่างจากปกติสำหรับลูปที่ทุกคนใช้เพราะฉันทำสิ่งต่าง ๆ ในพารามิเตอร์ที่ 3 และฉันก็ลดลงโดยตรงภายในอาเรย์

กล่าวว่าสิ่งนี้เป็นการยืนยันว่าเครื่องจักรเช่น -

เขียนว่าฉันคิดว่าจะทำให้สั้นลงเล็กน้อยและลบสิ่งที่ไร้ประโยชน์ออกและเขียนสิ่งนี้โดยใช้สไตล์เดียวกัน:

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

แม้ว่ามันจะสั้นกว่า แต่ดูเหมือนว่าการใช้iอีกครั้งจะช้าลงทุกอย่าง มัน 1/5 ช้ากว่าก่อนหน้านี้forวงและwhileหนึ่ง

หมายเหตุ:;เป็นสิ่งสำคัญมากหลังจากที่สำหรับ looo โดยไม่ต้อง{}

แม้ว่าฉันเพิ่งจะบอกคุณว่า jsperf ไม่ใช่วิธีที่ดีที่สุดในการทดสอบสคริปต์ .. ฉันได้เพิ่ม 2 ลูปที่นี่

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

และนี่คือคำตอบเกี่ยวกับประสิทธิภาพใน javascript อีกประการหนึ่ง

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

คำตอบนี้คือการแสดงวิธีการเขียนจาวาสคริปต์ ดังนั้นหากคุณไม่สามารถอ่านได้ให้ถามและคุณจะได้รับคำตอบหรืออ่านหนังสือเกี่ยวกับ javascript http://www.ecma-international.org/ecma-262/5.1/


คำตอบนี้เริ่มต้นของสิ่งที่ดีมาก ฉันสังเกตว่าสองสามปีที่ผ่านมาforนั้นเร็วกว่าwhileและฉันเคยอ่าน crome-dev มันเป็นเพราะเหตุผลที่คุณพูดถึง มันจะเป็นเพียงเรื่องของเวลาก่อนที่whileจะทันอีกครั้ง จากจุดนั้นไปตรรกะในส่วนแรกของคำตอบของคุณจะถูกระงับ (อีกครั้งใช่)! อย่างไรก็ตามการใช้งานที่ทันสมัยจะไม่ปฏิบัติตามทุกขั้นตอนที่ระบุอย่างเข้มงวดใน ecma อีกต่อไป (จะปรับให้เหมาะสม) ตั้งแต่ตอนนี้เครื่องยนต์ของคุณไม่ได้เป็นคอขวดที่สังเกตเห็นได้อีกต่อไปตอนนี้คุณสามารถสังเกตเห็นถึงการแคชของ CPU ในการย้อนกลับของลูป !
GitaarLAB

อธิบายดังนั้นบางทีฉันสามารถแก้ไขคำตอบหรือเรียนรู้สิ่งใหม่ btw คำตอบคือตอนนี้มากกว่าหนึ่งปี ... เบราว์เซอร์อาจมีการเปลี่ยนแปลงเมื่อเวลาผ่านไปเหมือนที่พวกเขาทำ ...
cocco

ในความคิดของฉันในขณะที่ (- ความยาว) เป็นสิ่งที่ชั่วร้ายเพราะในขณะที่มันใช้งานได้ทางเทคนิคเพราะ 0 เป็นเท็จ 0 และเท็จไม่ได้เป็นสิ่งเดียวกันจริง ๆ ที่พูดเชิงความหมาย
scott.korin

ใช่ ... มันเป็นโพสต์ที่เก่ากว่าตอนนี้ ... แต่ใช่ฉันรักความเรียบง่ายของในขณะที่ และแน่นอนเมื่อคุณพูดถึงมันในทั้งสองกรณีคุณจำเป็นต้องรู้ว่าจะเขียนอะไร ในอีกด้านหนึ่งฉันไม่เคยมีความจำเป็นต้องวนซ้ำจำนวนลบ
cocco

9
"เครื่องจักรชอบสิ่งนี้" ฟังดูเหมือนประโยคจากโฆษณาสำหรับผงซักฟอกซักผ้า
CocoaBean

11

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

การแก้ไขการทดสอบล่าสุดที่ฉันเตรียมไว้ (โดยการนำสิ่งที่เก่ากว่ากลับมาใช้ใหม่) แสดงให้เห็นสิ่งหนึ่ง

ความยาวแคชไม่สำคัญมากนัก แต่ไม่เป็นอันตราย

ทุกการทดสอบครั้งแรกที่ลิงก์ด้านบน (บนแท็บที่เปิดใหม่) ให้ผลลัพธ์ที่ดีที่สุดสำหรับตัวอย่าง 4 รายการสุดท้าย (อันดับ 3, 5, 7 และ 10 ในชาร์ต) ใน Chrome, Opera และ Firefox ใน Debian Squeeze 64-bit ( ฮาร์ดแวร์เดสก์ท็อปของฉัน ) การวิ่งครั้งต่อไปจะให้ผลลัพธ์ที่แตกต่างกันมาก

บทสรุปประสิทธิภาพที่ชาญฉลาดนั้นง่าย:

  • ไปกับการห่วง (ไปข้างหน้า) และการทดสอบโดยใช้แทน!==<
  • หากคุณไม่จำเป็นต้องนำอาร์เรย์มาใช้ใหม่ในภายหลังในขณะที่การวนซ้ำตามความยาวลดลงและshift()อาเรย์แบบทำลายนั้นก็มีประสิทธิภาพเช่นกัน

TL; DR

ทุกวันนี้ (2011.10) รูปแบบด้านล่างดูเหมือนจะเร็วที่สุด

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

โปรดทราบว่าการแคชarr.lengthไม่สำคัญที่นี่ดังนั้นคุณสามารถทดสอบi !== arr.lengthและประสิทธิภาพจะไม่ลดลง แต่คุณจะได้รับรหัสที่สั้นลง


PS: ฉันรู้ว่าในตัวอย่างที่มีshift()ผลของมันสามารถใช้แทนการเข้าถึงองค์ประกอบที่ 0 แต่ฉันมองข้ามอย่างใดหลังจากที่นำการแก้ไขก่อนหน้านี้กลับมาใช้ใหม่ (ซึ่งผิดในขณะที่ลูป) และต่อมาฉันไม่ต้องการสูญเสียผลลัพธ์ที่ได้


การสร้างตัวแปร insite วนรอบเช่นให้ปัจจุบัน = arr [i] สามารถลดประสิทธิภาพ (การจัดสรรหน่วยความจำขนาดใหญ่)? หรือจะเป็นการดีกว่าที่จะประกาศกระแสก่อนลูป? หรือใช้ arr [i] ในทุกที่ในวง?
Makarov Sergey

8

"ดีที่สุด" ในประสิทธิภาพบริสุทธิ์หรือไม่ หรือประสิทธิภาพและการอ่าน?

ประสิทธิภาพที่แท้จริง "ดีที่สุด" คือสิ่งนี้ซึ่งใช้แคชและตัวดำเนินการคำนำหน้า ++ (ข้อมูลของฉัน: http://jsperf.com/caching-array-length/189 )

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

ฉันจะยืนยันว่าแคชน้อยสำหรับห่วงเป็นสมดุลที่ดีที่สุดในเวลาดำเนินการและเวลาอ่านโปรแกรมเมอร์ โปรแกรมเมอร์ทุกคนที่เริ่มต้นด้วย C / C ++ / Java จะไม่เสีย ms ในการอ่านผ่านโปรแกรมนี้

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

2
+1 สำหรับการอ่าน ไม่ว่าlenจะตั้งชื่อได้ดีแค่ไหนคนหนึ่งจะต้องทำซ้ำสองครั้งในวงแรก ความตั้งใจของลูปที่สองนั้นชัดเจน
Josh Johnson

7

** แคชความยาวของอาร์เรย์ภายในลูปบางวินาทีจะถูกลบออก ขึ้นอยู่กับรายการในอาเรย์ถ้ามีไอเท็มในอาเรย์มากกว่านั้นมีความแตกต่างที่สำคัญเกี่ยวกับ Ms ของเวลา *

**

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***

**


6

นี้ดูเหมือนจะเป็นวิธีที่เร็วที่สุดโดยไกล ...

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

คำนึงว่าสิ่งนี้จะใช้อาร์เรย์กินมันและไม่เหลืออะไรเลย ...


2
arr.shift();แทนที่จะเป็นarr.pop() เพื่อให้สามารถย้อนกลับอาร์เรย์ได้
Tintu C Raju

1
@Gargaroz หากคุณได้รับ JSON จาก webservice เช่นบริการแชทหรือรายการในแค็ตตาล็อกผลิตภัณฑ์ สถานการณ์อื่นเมื่อคุณต้องการใช้อาร์เรย์เพียงครั้งเดียวสามารถเป็นตัวอย่างแผนภูมิที่ได้รับพิกัดจำนวนมากบนพื้นฐานของ intervall มีตัวอย่างมากมาย
Sergio

เจ๋งขอบคุณสำหรับคำอธิบายคุณใจดีมาก คุณสามารถชี้ให้ฉันในทิศทางที่ฉันอาจพบตัวอย่างเพิ่มเติมเพื่อใช้ประโยชน์จากการวนลูปแบบนี้?
Gargaroz

1
ขณะนี้อยู่ใน Chrome 53 และ Firefox 48 เป็นวิธีที่ช้าที่สุด - ตรวจสอบperfjs.info/array-iteration
Pencroff

1
@Alireza เห็นด้วยฉันมีความคิดเห็นสำหรับคำตอบของฉันด้วย
Sergio

4

มันปี2017

ฉันทำแบบทดสอบ

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

ดูเหมือนว่าwhileวิธีนี้จะเร็วที่สุดใน Chrome

ดูเหมือนว่าพร่องซ้าย ( --i) จะเร็วกว่าคนอื่น ๆ ( ++i, i--, i++) บน Firefox

วิธีนี้เป็นการอดอาหารโดยเฉลี่ย แต่มันวนซ้ำในลำดับที่กลับรายการ

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

หากคำสั่งซื้อล่วงหน้ามีความสำคัญให้ใช้วิธีการนี้

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

3
การใช้คำหลักletเป็นการเปรียบเทียบประสิทธิภาพการสร้างขอบเขตแทนการวนซ้ำ การใช้ลูปlet i = 0, ii = array.lengthของคุณforจะสร้างขอบเขตใหม่สำหรับตัวแปรเหล่านั้นในforบล็อก whileตัวอย่างของคุณไม่ได้สร้างขอบเขตใหม่สำหรับตัวแปรภายในwhileบล็อกและนั่นเป็นสาเหตุที่ทำให้มันเร็วขึ้น หากคุณใช้varแทนletการวนซ้ำคุณจะเห็นว่าการวนซ้ำยังคงเร็วเท่ากับปี 2017 แต่อ่านได้มากขึ้น
CGodo

นี่คือ jsperf ของสิ่งที่ฉันกำลังพูดถึง: jsperf.com/javascript-loop-testing-let-vs-var
CGodo

นี่เป็นเพียงปัญหาใน Chrome ในเบราว์เซอร์อื่นvarและletมีประสิทธิภาพเหมือนกัน - stackoverflow.com/a/32345435/1785975
SeregPie

น่าสนใจ อย่างไรก็ตามฉันไม่พบคำว่า " whileกำลังเร็วขึ้นใน Chrome" อย่างแม่นยำ ใช้เฉพาะเมื่อใช้letเนื่องจากปัญหาประสิทธิภาพของคำหลักนั้นใน Chrome หากใช้varหรือใช้กับเบราว์เซอร์อื่นforและwhileค่อนข้างเหมือนกันบางครั้งforก็เร็วกว่าขึ้นอยู่กับเกณฑ์มาตรฐานและมันก็มีขนาดกะทัดรัดและสามารถอ่านได้มากขึ้น
CGodo

2

ฉันมักจะเขียนในสไตล์แรกเสมอ

แม้ว่าคอมไพเลอร์ฉลาดพอที่จะปรับให้เหมาะสมสำหรับอาร์เรย์ แต่ก็ยังฉลาดถ้าเราใช้ DOMNodeList ที่นี่หรือวัตถุที่ซับซ้อนที่มีความยาวที่คำนวณได้หรือไม่

ฉันรู้ว่าคำถามคืออะไรเกี่ยวกับอาร์เรย์ แต่ฉันคิดว่ามันเป็นวิธีปฏิบัติที่ดีในการเขียนลูปทั้งหมดของคุณในรูปแบบเดียว


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

i ++ เร็วกว่า ++ i, --i และ i--

นอกจากนี้คุณสามารถบันทึกบรรทัดสุดท้ายที่ทำ arr [i ++] ในครั้งสุดท้ายที่คุณจำเป็นต้องเข้าถึง i (แต่อาจเป็นการยากที่จะดีบัก)

คุณสามารถทดสอบได้ที่นี่ (พร้อมการทดสอบลูปอื่น ๆ ): http://jsperf.com/for-vs-whilepop/5


1
ขณะนี้อยู่ใน Chrome 53 เป็นจริง แต่ Firefox 48 มีความเร็วเท่ากัน - ตรวจสอบperfjs.info/array-iteration
Pencroff

thunderguy.com/semicolon/2002/08/13/…พูด++iได้เร็วขึ้น ...
IMTheNachoMan

1

เมื่อวันที่กันยายน 2017 การทดสอบ jsperf เหล่านี้แสดงรูปแบบต่อไปนี้ให้มีประสิทธิภาพมากที่สุดใน Chrome 60:

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

ใครสามารถทำซ้ำได้บ้าง


ใช่ดูเหมือนว่าจะเร็วที่สุด แต่ลองใช้งานใน IE11 และตัวเลือกเหล่านี้จะช้าที่สุด และใน Firefox 55.03 'เลนส์แคชเก่าที่ถูกจับ' จะสูงถึง 12mil ซึ่งมีประสิทธิภาพสูงกว่าโครเมี่ยม 3.3k เพื่อให้สอดคล้องกับประสิทธิภาพในทุกเบราว์เซอร์คุณควรใช้ลูปเฉลี่ยที่เร็วที่สุดสำหรับทุกเบราว์เซอร์
Plippie

0

ฉันได้ลองวิธีอื่น ๆ ในการย้ำอาร์เรย์ขนาดใหญ่และพบว่าการลดความยาวของอาร์เรย์ลงครึ่งหนึ่งแล้ววนซ้ำทั้งสองครึ่งในการวนซ้ำจะเร็วขึ้น แตกต่างประสิทธิภาพนี้สามารถเห็นได้ในขณะที่การประมวลผลอาร์เรย์ขนาดใหญ่

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++; 
  }
}

การเปรียบเทียบประสิทธิภาพบางอย่าง (โดยใช้ timer.js) ระหว่างความยาวแคชของ for-loop VS วิธีการด้านบน

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


0

การทดสอบอื่น ๆ ของ jsperf.com: http://jsperf.com/while-reverse-vs-for-cached-length

การย้อนกลับในขณะที่ลูปดูเหมือนจะเร็วที่สุด ปัญหาเดียวคือในขณะที่ (--i) จะหยุดที่ 0 ฉันจะเข้าถึงอาร์เรย์ [0] ในวงของฉันได้อย่างไร


2
หากคุณทำเช่นwhile (i--)นั้นความจริงของiจะถูกทดสอบก่อนที่จะลดลงมากกว่าที่จะลดลงและจากนั้นทดสอบความจริง
Justin Fisher


0

ในขณะที่การวนรอบนั้นเร็วกว่าการวนซ้ำเล็กน้อย

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

ใช้ในขณะที่ลูปแทน



-1

ทางออกที่ดีที่สุดที่ฉันรู้คือใช้แผนที่

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

46
คำถามไม่ได้ถามหาวิธีที่ช้าที่สุดในการวนซ้ำวนวน
eoleary


-1

วิธีที่เร็วกว่าในการวนซ้ำในอาร์เรย์คือการใช้ตัวกรอง ตัวกรอง () วิธีการสร้างอาร์เรย์ใหม่ที่มีองค์ประกอบทั้งหมดที่ผ่านการทดสอบการใช้งานโดยฟังก์ชั่นที่ให้ไว้

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

const words = ['Floccinaucinihilipilification', 'limit', 'elite', 'Hippopotomonstrosesquipedaliophobia', 'destruction', 'present'];

const result = words.filter(word => word.length > 6);

console.log(new Date(), result);

จากประสบการณ์ของฉันฉันมักจะชอบตัวกรองแผนที่ ฯลฯ


คำถามเกี่ยวกับการวนซ้ำอาร์เรย์ในระยะเวลาที่สั้นที่สุดซึ่งไม่ได้คัดลอกอาร์เรย์ไปยังอาร์เรย์ใหม่
ราหุลคาดูคา

-1

ตั้งแต่ 2019 WebWorker ได้รับความนิยมมากขึ้นสำหรับชุดข้อมูลขนาดใหญ่เราสามารถใช้ WebWorker เพื่อประมวลผลเร็วขึ้นมากโดยใช้โปรเซสเซอร์แบบมัลติคอร์อย่างเต็มที่

นอกจากนี้เรายังมีParallel.jsซึ่งทำให้ WebWorker ง่ายต่อการใช้งานสำหรับการประมวลผลข้อมูล

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.