Là các vòng lặp thực sự nhanh hơn trong đảo ngược?


268

Tôi đã nghe điều này khá nhiều lần. Các vòng lặp JavaScript có thực sự nhanh hơn khi đếm ngược không? Nếu vậy, tại sao? Tôi đã thấy một vài ví dụ về bộ thử nghiệm cho thấy các vòng lặp đảo ngược nhanh hơn, nhưng tôi không thể tìm thấy bất kỳ lời giải thích nào về lý do!

Tôi cho rằng đó là vì vòng lặp không còn phải đánh giá một thuộc tính mỗi lần nó kiểm tra xem nó đã kết thúc hay chưa và nó chỉ kiểm tra giá trị số cuối cùng.

I E

for (var i = count - 1; i >= 0; i--)
{
  // count is only evaluated once and then the comparison is always on 0.
}

6
hehe điều đó sẽ mất thời gian thử i--
StampedeXV

5
forVòng lặp ngược nhanh hơn vì biến điều khiển vòng lặp giới hạn trên (hehe, giới hạn dưới) không phải được xác định hoặc tìm nạp từ một đối tượng; nó là một số không.
Josh Stodola

88
Không có sự khác biệt thực sự . Cấu trúc vòng lặp gốc luôn luôn sẽ rất nhanh . Đừng lo lắng về hiệu suất của họ.
James Allardice

24
@Afshin: Đối với các câu hỏi như thế này, vui lòng cho chúng tôi xem các bài viết bạn đang đề cập đến.
Các cuộc đua nhẹ nhàng trong quỹ đạo

22
Có sự khác biệt chủ yếu quan trọng đối với các thiết bị rất thấp và chạy bằng pin. Sự khác biệt là với i-- bạn so sánh với 0 ở cuối vòng lặp, trong khi với i ++, bạn so sánh với số> 0. Tôi tin rằng sự khác biệt về hiệu suất là khoảng 20 nano giây (tương tự như axp ax, 0 so với cmp ax , bx) - không có gì nhưng nếu bạn lặp hàng nghìn lần mỗi giây, tại sao không đạt được 20 nano giây cho mỗi lần :)
Luben

Câu trả lời:


903

Nó không phải i--là nhanh hơn i++. Thật ra, cả hai đều nhanh như nhau.

Những gì cần có thời gian trong các vòng tăng dần là đánh giá, cho mỗi i, kích thước của mảng của bạn. Trong vòng lặp này:

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

Bạn .lengthchỉ đánh giá một lần, khi bạn khai báo i, trong khi đối với vòng lặp này

for(var i = 1; i <= array.length; i++)

bạn đánh giá .lengthmỗi lần bạn tăng i, khi bạn kiểm tra xem i <= array.length.

Trong hầu hết các trường hợp, bạn thậm chí không nên lo lắng về loại tối ưu hóa này .


23
Có đáng để giới thiệu một biến cho mảng.length và sử dụng nó trong phần đầu của vòng lặp không?
ragatskynet

39
@ragatskynet: Không, trừ khi bạn đang thiết lập một điểm chuẩn và muốn tạo một điểm.
Jon

29
@ragatskynet Nó phụ thuộc: sẽ nhanh hơn để đánh giá .lengthmột số lần nhất định hoặc khai báo và xác định một biến mới? Trong hầu hết các trường hợp, đây là tối ưu hóa sớm (và sai), trừ khi bạn lengthrất tốn kém để đánh giá.
alestanis

10
@ Dr.Dredel: Đây không phải là sự so sánh - đó là sự đánh giá. 0là nhanh hơn để đánh giá hơn array.length. Vâng, được cho là.
Các cuộc đua nhẹ nhàng trong quỹ đạo

22
Điều đáng nói là chúng ta đang nói về các ngôn ngữ được giải thích như Ruby, Python. Các ngôn ngữ được biên dịch, ví dụ Java có tối ưu hóa ở cấp độ trình biên dịch, điều này sẽ "làm mịn" những khác biệt này đến mức không thành vấn đề nếu .lengthcó khai báo for loophay không.
Haris Krajina

198

Anh chàng này đã so sánh rất nhiều vòng lặp trong javascript, trong rất nhiều trình duyệt. Anh ấy cũng có một bộ thử nghiệm để bạn có thể tự chạy chúng.

Trong mọi trường hợp (trừ khi tôi bỏ lỡ một trong lần đọc), vòng lặp nhanh nhất là:

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

Đẹp :) Nhưng không có "lạc hậu" nào được thử nghiệm ... Nhưng các vòng lặp được đề cập bởi peirix hoặc searlea sẽ khá giống với vòng lặp "while" với "i--" như điều kiện của nó. Và đó là vòng lặp nhanh nhất được thử nghiệm.
SvenFinke

11
Thú vị, nhưng tôi tự hỏi nếu trước khi giảm sẽ còn nhanh hơn. Vì nó sẽ không phải lưu trữ giá trị trung gian của i.
tvanfosson

Nếu tôi nhớ chính xác, giáo sư khóa học phần cứng của tôi nói, bài kiểm tra 0 hoặc không 0 là "tính toán" nhanh nhất có thể. Trong khi (i--) bài kiểm tra luôn là bài kiểm tra cho 0. Có lẽ đó là lý do tại sao điều này nhanh nhất?
StampedeXV

3
@tvanfosson nếu bạn giảm trước --ithì bạn phải sử dụng var current = arr[i-1];bên trong vòng lặp hoặc nó sẽ bị tắt bởi một ...
DaveAlger

2
Tôi đã nghe nói rằng tôi-- có thể nhanh hơn - vì trong trường hợp thứ hai, bộ xử lý cần giảm và sau đó kiểm tra giá trị mới (có sự phụ thuộc dữ liệu giữa các hướng dẫn), trong trường hợp đầu tiên, bộ xử lý có thể kiểm tra giá trị hiện có và giảm giá trị một thời gian sau đó. Tôi không chắc chắn nếu điều này áp dụng cho JavaScript hoặc chỉ mã C mức độ rất thấp.
marcus

128

Tôi cố gắng đưa ra một bức tranh rộng lớn với câu trả lời này.

Những suy nghĩ sau đây trong ngoặc niềm tin của tôi cho đến khi tôi vừa mới kiểm tra vấn đề:

[[Xét về các ngôn ngữ cấp thấp như C / C ++ , mã được biên dịch để bộ xử lý có lệnh nhảy có điều kiện đặc biệt khi một biến bằng 0 (hoặc khác không).
Ngoài ra, nếu bạn quan tâm đến việc tối ưu hóa nhiều này, bạn có thể đi ++ithay i++vì bởi vì đó ++ilà một lệnh của bộ xử lý trong khi đó i++có nghĩa là j=i+1, i=j.]]

Các vòng lặp thực sự nhanh có thể được thực hiện bằng cách hủy đăng ký chúng:

for(i=800000;i>0;--i)
    do_it(i);

Nó có thể chậm hơn

for(i=800000;i>0;i-=8)
{
    do_it(i); do_it(i-1); do_it(i-2); ... do_it(i-7);
}

nhưng lý do cho việc này có thể khá phức tạp (chỉ cần đề cập đến, có các vấn đề về xử lý tiền xử lý lệnh và xử lý bộ đệm trong trò chơi).

Về các ngôn ngữ cấp cao , như JavaScript như bạn đã hỏi, bạn có thể tối ưu hóa mọi thứ nếu bạn dựa vào các thư viện, các hàm tích hợp để lặp. Hãy để họ quyết định làm thế nào nó được thực hiện tốt nhất.

Do đó, trong JavaScript, tôi sẽ đề xuất sử dụng một cái gì đó như

array.forEach(function(i) {
    do_it(i);
});

Nó cũng ít bị lỗi hơn và các trình duyệt có cơ hội tối ưu hóa mã của bạn.

[LƯU Ý: không chỉ các trình duyệt, mà bạn cũng có một không gian để tối ưu hóa dễ dàng, chỉ cần xác định lại forEachchức năng (phụ thuộc vào trình duyệt) để nó sử dụng thủ thuật tốt nhất mới nhất! :) @AMK nói trong trường hợp đặc biệt, nó đáng để sử dụng array.pophoặc array.shift. Nếu bạn làm điều đó, đặt nó đằng sau bức màn. Quá mức cần thiết là thêm các tùy chọn forEachđể chọn thuật toán lặp.]

Hơn nữa, đối với các ngôn ngữ cấp thấp, cách tốt nhất là sử dụng một số chức năng thư viện thông minh cho các hoạt động phức tạp, lặp nếu có thể.

Những thư viện đó cũng có thể đặt mọi thứ (đa luồng) phía sau lưng của bạn và các lập trình viên chuyên ngành luôn cập nhật chúng.

Tôi đã xem xét kỹ hơn một chút và hóa ra trong C / C ++, ngay cả đối với các hoạt động 5e9 = (50.000x100.000), không có sự khác biệt giữa đi lên và xuống nếu thử nghiệm được thực hiện theo hằng số như @alestanis nói. (Kết quả của JsPerf đôi khi không nhất quán nhưng nói chung cũng giống nhau: bạn không thể tạo ra sự khác biệt lớn.)
Vì vậy, --itình cờ là một điều "posh". Nó chỉ làm cho bạn trông giống như một lập trình viên tốt hơn. :)

Mặt khác, để không kiểm soát trong tình huống 5e9 này, nó đã đưa tôi xuống từ 12 giây xuống 2,5 giây khi tôi đi được 10 giây và xuống còn 2,1 giây khi tôi đi được 20 giây. Đó là không có tối ưu hóa, và tối ưu hóa đã đưa mọi thứ xuống thời gian ngắn không thể đo lường được. :) (Việc hủy đăng ký có thể được thực hiện theo cách của tôi ở trên hoặc sử dụng i++, nhưng điều đó không mang lại những điều phía trước trong JavaScript.)

Tất cả trong tất cả: giữ i--/ i++++i/ i++sự khác biệt cho các cuộc phỏng vấn công việc, bám sát array.forEachhoặc các chức năng thư viện phức tạp khác khi có sẵn. ;)


18
Từ khóa là "có thể". Vòng lặp không được kiểm soát của bạn cũng có thể chậm hơn vòng lặp ban đầu. Khi tối ưu hóa, luôn luôn đo lường để bạn biết chính xác những gì tác động đến những thay đổi của bạn.
jalf

1
@jalf đúng là thật, +1. Các chiều dài khác nhau (> = 1) khác nhau về hiệu quả. Đó là lý do tại sao sẽ thuận tiện hơn khi để công việc này đến thư viện nếu có thể, chưa kể các trình duyệt chạy trên các kiến ​​trúc khác nhau để có thể tốt hơn nếu họ quyết định cách làm array.each(...). Tôi không nghĩ rằng họ sẽ thử và thử nghiệm với các vòng lặp đơn giản.
Barney

1
@BarnabasSzabolcs Câu hỏi dành riêng cho JavaScript, không phải C hoặc các ngôn ngữ khác. Trong JS có một sự khác biệt, vui lòng xem câu trả lời của tôi dưới đây. Mặc dù không áp dụng cho câu hỏi, +1 câu trả lời tốt!
AMK

1
Và ở đó bạn đi - +1để có được một Great AnswerHuy hiệu. Câu trả lời thực sự tuyệt vời. :)
Rohit Jain

1
APL / A ++ cũng không phải là ngôn ngữ. Mọi người sử dụng những điều này để thể hiện rằng họ không quan tâm đến các chi tiết cụ thể về ngôn ngữ, nhưng điều mà các ngôn ngữ đó chia sẻ.
Barney

33

i-- nhanh như i++

Mã dưới đây nhanh như mã của bạn, nhưng sử dụng một biến phụ:

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

Khuyến nghị là KHÔNG đánh giá kích thước của mảng mỗi lần. Đối với mảng lớn người ta có thể thấy sự suy giảm hiệu suất.


6
Bạn hoàn toàn sai ở đây. Bây giờ kiểm soát vòng lặp cần thêm biến nội bộ (i trở lên) và tùy thuộc vào công cụ JavaScript, điều này có thể hạn chế tiềm năng để tối ưu hóa mã. JIT sẽ dịch các vòng lặp đơn giản sang các opcode máy trực tiếp, nhưng nếu các vòng lặp có quá nhiều biến số thì JIT sẽ không thể tối ưu hóa tốt như vậy. Nói chung, nó bị giới hạn bởi các thanh ghi kiến ​​trúc hoặc cpu mà JIT sử dụng. Khởi tạo một lần và đi xuống đơn giản là giải pháp sạch nhất và đó là lý do tại sao nó được đề xuất ở mọi nơi.
Pavel P

Biến bổ sung sẽ được loại bỏ bởi trình tối ưu hóa của trình thông dịch / trình biên dịch chung. Vấn đề là 'so sánh với 0' - xem câu trả lời của tôi để được giải thích thêm.
H.-Dirk Schmitt

1
Tốt hơn là đặt 'lên' trong vòng lặp:for (var i=0, up=Things.length; i<up; i++) {}
thdoan

22

Vì, bạn quan tâm đến chủ đề này, hãy xem bài đăng trên web của Greg Reimer về điểm chuẩn của vòng lặp JavaScript, Cách nhanh nhất để mã hóa một vòng lặp trong JavaScript là gì? :

Tôi đã xây dựng một bộ kiểm tra điểm chuẩn vòng lặp cho các cách khác nhau của các vòng lặp mã hóa trong JavaScript. Hiện tại đã có một vài trong số này, nhưng tôi không tìm thấy bất kỳ điều gì thừa nhận sự khác biệt giữa mảng gốc và bộ sưu tập HTML.

Bạn cũng có thể thực hiện kiểm tra hiệu suất trên một vòng lặp bằng cách mở https://blogs.oracle.com/greimer/resource/loop-test.html(không hoạt động nếu JavaScript bị chặn trong trình duyệt, ví dụ như NoScript ).

BIÊN TẬP:

Một điểm chuẩn gần đây hơn được tạo bởi Milan Adamovsky có thể được thực hiện trong thời gian chạy tại đây cho các trình duyệt khác nhau.

Để thử nghiệm trong Firefox 17.0 trên Mac OS X 10.6, tôi đã có vòng lặp sau:

var i, result = 0;
for (i = steps - 1; i; i--) {
  result += i;
}

như trước nhanh nhất bởi:

var result = 0;
for (var i = steps - 1; i >= 0; i--) {
  result += i;
}

Ví dụ điểm chuẩn.


5
Bài đó đã 4 tuổi rồi. Với tốc độ (ha!) Tại đó các công cụ JavaScript đã được cập nhật, hầu hết các lời khuyên có thể đã lỗi thời - bài viết thậm chí không đề cập đến Chrome và công cụ JavaScript V8 sáng bóng của nó.
Yi Jiang

Đúng, tôi nhớ chi tiết đó, cảm ơn vì đã chỉ ra điều đó. Sự kiện tại thời điểm đó không có nhiều sự khác biệt giữa --i hoặc i ++ trên vòng lặp cho.
dreamcrash

19

Nó không phải là --hay ++, nó là hoạt động so sánh. Với --bạn có thể sử dụng so sánh với 0, trong khi với ++bạn cần so sánh nó với độ dài. Trên bộ xử lý, so sánh với số 0 thường có sẵn, trong khi so sánh với số nguyên hữu hạn yêu cầu phép trừ.

a++ < length

thực sự được biên dịch thành

a++
test (a-length)

Vì vậy, nó mất nhiều thời gian hơn trên bộ xử lý khi biên dịch.


15

Câu trả lời ngắn

Đối với mã thông thường, đặc biệt là trong một ngôn ngữ cấp cao như JavaScript , không có sự khác biệt về hiệu năng trong i++i--.

Các tiêu chí hiệu suất là việc sử dụng trong forvòng lặp và câu lệnh so sánh .

Điều này áp dụng cho tất cả các ngôn ngữ cấp cao và chủ yếu độc lập với việc sử dụng JavaScript. Giải thích là mã trình biên dịch kết quả ở dòng dưới cùng.

Giải thích chi tiết

Một sự khác biệt hiệu suất có thể xảy ra trong một vòng lặp. Nền tảng là ở cấp trình biên dịch , bạn có thể thấy rằng đó compare with 0chỉ là một câu lệnh không cần đăng ký bổ sung.

So sánh này được ban hành trên mỗi lần vượt qua vòng lặp và có thể dẫn đến cải thiện hiệu suất có thể đo lường được.

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

sẽ được đánh giá thành mã giả như thế này:

 i=array.length
 :LOOP_START
 decrement i
 if [ i = 0 ] goto :LOOP_END
 ... BODY_CODE
 :LOOP_END

Lưu ý rằng 0 là một nghĩa đen, hay nói cách khác, là một giá trị không đổi.

for(var i = 0 ; i < array.length; i++ )

sẽ được đánh giá thành mã giả như thế này (tối ưu hóa trình thông dịch bình thường):

 end=array.length
 i=0
 :LOOP_START
 if [ i < end ] goto :LOOP_END
 increment i
 ... BODY_CODE
 :LOOP_END

Lưu ý rằng kết thúc là một biến cần một thanh ghi CPU . Điều này có thể gọi một hoán đổi đăng ký bổ sung trong mã và cần một câu lệnh so sánh đắt hơn trong ifcâu lệnh.

Chỉ 5 xu của tôi

Đối với một ngôn ngữ cấp cao, khả năng đọc, tạo điều kiện cho khả năng duy trì, quan trọng hơn là một cải tiến hiệu suất nhỏ.

Thông thường việc lặp cổ điển từ đầu đến cuối là tốt hơn.

Lặp lại nhanh hơn từ cuối mảng để bắt đầu kết quả trong chuỗi đảo ngược có thể không mong muốn.

Đoạn tái bút

Như được hỏi trong một nhận xét: Sự khác biệt của --ii--là trong đánh giá itrước hoặc sau khi giảm.

Giải thích tốt nhất là dùng thử ;-) Đây là một ví dụ về Bash .

 % i=10; echo "$((--i)) --> $i"
 9 --> 9
 % i=10; echo "$((i--)) --> $i"
 10 --> 9

1
1+ Giải thích tốt. Chỉ cần một câu hỏi ngoài phạm vi, bạn có thể vui lòng giải thích sự khác biệt giữa --ii--cũng không?
Afshin Mehrabani

14

Tôi đã thấy đề xuất tương tự trong Văn bản cao siêu 2.

Giống như đã nói, cải tiến chính là không đánh giá độ dài của mảng tại mỗi lần lặp trong vòng lặp for. Đây là một kỹ thuật tối ưu hóa nổi tiếng và đặc biệt hiệu quả trong JavaScript khi mảng là một phần của tài liệu HTML (thực hiện forcho tất cả các liyếu tố).

Ví dụ,

for (var i = 0; i < document.getElementsByTagName('li').length; i++)

chậm hơn nhiều so với

for (var i = 0, len = document.getElementsByTagName('li').length; i < len; i++)

Từ nơi tôi đang đứng, sự cải thiện chính trong biểu mẫu trong câu hỏi của bạn là thực tế là nó không khai báo một biến phụ ( lentrong ví dụ của tôi)

Nhưng nếu bạn hỏi tôi, toàn bộ vấn đề không phải là về tối ưu hóa i++so với i--tối ưu hóa, mà là không phải đánh giá độ dài của mảng ở mỗi lần lặp (bạn có thể xem bài kiểm tra điểm chuẩn trên jsperf ).


1
Tôi phải lấy ngoại lệ cho từ tính toán ở đây. Xem nhận xét của tôi về câu trả lời của Pavel . Thông số ECMA nói rằng độ dài mảng không được tính khi bạn tham chiếu đến nó.
kojiro

'Đánh giá' sẽ là một lựa chọn tốt hơn? Thực tế thú vị btw, tôi không biết điều đó
BBog

Biến bổ sung sẽ được loại bỏ bởi trình tối ưu hóa của trình thông dịch / trình biên dịch chung. Điều tương tự áp dụng cho việc đánh giá mảng.length. Vấn đề là 'so sánh với 0' - xem câu trả lời của tôi để được giải thích thêm.
H.-Dirk Schmitt

@ H.-DirkSchmitt câu trả lời thông thường của bạn sang một bên, từ lâu trong lịch sử JavaScript, trình biên dịch đã không tối ưu hóa chi phí hiệu năng của chuỗi tra cứu. AFAIK V8 là người đầu tiên thử.
kojiro

@ H.-DirkSchmitt giống như kojiro đã nói, đây là một thủ thuật nổi tiếng và được thiết lập tốt. Ngay cả khi nó không còn phù hợp trong các trình duyệt hiện đại, điều đó vẫn không làm cho nó trở nên lỗi thời. Bên cạnh đó, thực hiện bằng cách giới thiệu một biến mới theo chiều dài hoặc với mẹo trong câu hỏi của OP, đó vẫn là cách tốt nhất, imo. Đó chỉ là một điều thông minh để làm và thực hành tốt, tôi không nghĩ nó có liên quan gì đến thực tế là trình biên dịch đã được tối ưu hóa để xử lý một cái gì đó thường được thực hiện theo cách xấu trong JS
BBog

13

Tôi không nghĩ rằng nó có ý nghĩa để nói rằng i--nhanh hơn i++trong JavaScript.

Trước hết , nó hoàn toàn phụ thuộc vào việc triển khai công cụ JavaScript.

Thứ hai , với điều kiện là các cấu trúc đơn giản nhất JIT'ed và được dịch sang các hướng dẫn riêng, thì i++vs i--sẽ hoàn toàn phụ thuộc vào CPU thực thi nó. Đó là, trên ARM (điện thoại di động), tốc độ xuống 0 nhanh hơn kể từ khi giảm và so sánh với 0 được thực hiện trong một lệnh.

Có lẽ, bạn nghĩ rằng cái này đã hơn cái kia bởi vì cách được đề xuất là

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

nhưng cách được đề xuất không phải vì cái này nhanh hơn cái kia, mà đơn giản là vì nếu bạn viết

for(var i = 0; i < array.length; i++)

sau đó trên mỗi lần lặp array.lengthphải được đánh giá (công cụ JavaScript thông minh hơn có lẽ có thể tìm ra rằng vòng lặp sẽ không thay đổi độ dài của mảng). Mặc dù trông giống như một câu lệnh đơn giản, nhưng thực sự đó là một số chức năng được công cụ JavaScript gọi dưới mui xe.

Lý do khác, tại sao i--có thể được coi là "nhanh hơn" là vì công cụ JavaScript chỉ cần phân bổ một biến nội bộ để kiểm soát vòng lặp (biến thành var i). Nếu bạn so sánh với Array.length hoặc với một số biến khác thì phải có nhiều hơn một biến nội bộ để kiểm soát vòng lặp và số lượng biến nội bộ là tài sản giới hạn của công cụ JavaScript. Càng ít biến được sử dụng trong một vòng lặp, JIT càng có nhiều cơ hội để tối ưu hóa. Đó là lý do tại sao i--có thể được coi là nhanh hơn ...


3
Có lẽ đáng để phân tích cẩn thận về cách array.lengthđánh giá. Độ dài không được tính khi bạn tham khảo nó. (Đây chỉ là một thuộc tính được đặt bất cứ khi nào một chỉ mục mảng được tạo hoặc thay đổi ). Khi có thêm chi phí, đó là do công cụ JS chưa tối ưu hóa chuỗi tra cứu cho tên đó.
kojiro

Chà, không chắc thông số kỹ thuật của Ecma nói gì, nhưng biết về một số phần bên trong của các công cụ JavaScript khác nhau thì không đơn giản getLength(){return m_length; }vì có một số công việc vệ sinh liên quan. Nhưng nếu bạn cố gắng nghĩ ngược lại: điều đó sẽ khá khéo léo khi viết triển khai mảng trong đó độ dài cần phải được tính toán thực sự :)
Pavel P

thông số ECMA yêu cầu thuộc tính độ dài đã được tính toán. Các lengthphải được cập nhật ngay lập tức bất cứ khi nào một tài sản mà-là-một-mảng-index được thêm vào hoặc thay đổi.
kojiro

Điều tôi đang cố gắng nói là thật khó để vi phạm thông số kỹ thuật nếu bạn thử nghĩ về nó.
Pavel P

1
x86 giống như ARM về mặt đó. dec/jnzvs inc eax / cmp eax, edx / jne.
Peter Cordes

13

Vì không có câu trả lời nào khác có vẻ trả lời câu hỏi cụ thể của bạn (hơn một nửa trong số chúng hiển thị các ví dụ C và thảo luận về các ngôn ngữ cấp thấp hơn, câu hỏi của bạn là dành cho JavaScript), tôi quyết định tự viết.

Vì vậy, ở đây bạn đi:

Câu trả lời đơn giản: i-- thường nhanh hơn vì không phải chạy so sánh 0 mỗi lần chạy, kết quả kiểm tra trên các phương pháp khác nhau như sau:

Kết quả kiểm tra: Như đã được "chứng minh" bởi jsPerf này , arr.pop()thực sự là vòng lặp nhanh nhất cho đến nay. Nhưng, tập trung vào --i, i--, i++++inhư bạn hỏi trong câu hỏi của bạn, sau đây là jsPerf (họ đến từ nhiều jsPerf của, vui lòng xem các nguồn dưới đây) kết quả tóm tắt:

--ii--giống nhau trong Firefox trong khi i--Chrome nhanh hơn.

Trong Chrome, vòng lặp cơ bản ( for (var i = 0; i < arr.length; i++)) nhanh hơn i----itrong Firefox thì chậm hơn.

Trong cả Chrome và Firefox, bộ nhớ cache arr.lengthnhanh hơn đáng kể với Chrome đi trước khoảng 170.000 op / giây.

Không có sự khác biệt đáng kể, ++inhanh hơn i++ở hầu hết các trình duyệt, AFAIK, nó không bao giờ là cách khác trong bất kỳ trình duyệt nào.

Tóm tắt ngắn hơn: arr.pop() là vòng lặp nhanh nhất cho đến nay; đối với các vòng lặp được đề cập cụ thể, i--là vòng lặp nhanh nhất.

Nguồn: http://jsperf.com/fastest-array-loops-in-javascript/15 , http://jsperf.com/ipp-vs-ppi-2

Tôi mong bạn trả lời câu hỏi này.


1
Có vẻ như popbài kiểm tra của bạn dường như chỉ khá nhanh vì nó đang giảm kích thước mảng xuống 0 cho phần lớn vòng lặp - theo như tôi có thể nói. Tuy nhiên để đảm bảo mọi thứ đều công bằng, tôi đã thiết kế jsperf này để tạo ra mảng theo cùng một cách với mỗi thử nghiệm - có vẻ .shift()như là người chiến thắng cho một vài trình duyệt của tôi - không phải là điều tôi mong đợi mặc dù :) jsperf.com / so sánh-các loại khác nhau của vòng lặp
Pebbl

Được ủng hộ vì là người duy nhất đề cập đến ++i: D
jaggedsoft

12

Nó phụ thuộc vào vị trí của mảng của bạn trong bộ nhớ và tỷ lệ trúng của các trang bộ nhớ trong khi bạn đang truy cập vào mảng đó.

Trong một số trường hợp, việc truy cập các thành viên mảng theo thứ tự cột nhanh hơn thứ tự hàng vì tỷ lệ trúng tăng.


1
Nếu chỉ có OP đã hỏi tại sao đi ngang qua cùng một ma trận theo các đơn hàng khác nhau thì có thể mất nhiều thời gian khác nhau ..
dcow

1
Xét rằng trong Hệ điều hành, việc quản lý bộ nhớ của họ dựa trên Paging, do đó, khi một quá trình cần dữ liệu không có trong các trang được lưu trong bộ nhớ cache, sẽ xảy ra lỗi trang trong HĐH và do đó phải đưa trang đích vào bộ đệm CPU và thay thế bằng một trang khác, do đó, gây ra chi phí xử lý mất nhiều thời gian hơn khi trang đích nằm trong bộ đệm CPU. Giả sử rằng chúng tôi đã xác định một mảng lớn mà mỗi hàng trong nó lớn hơn kích thước hệ điều hành trang và chúng tôi truy cập nó theo thứ tự hàng, trong trường hợp này tỷ lệ lỗi trang tăng và kết quả chậm hơn so với truy cập thứ tự cột vào mảng đó.
Akbar Bayati

Lỗi trang không giống như lỗi bộ nhớ cache. Bạn chỉ bị lỗi trang nếu bộ nhớ của bạn bị phân trang ra đĩa. Truy cập các mảng nhiều chiều theo thứ tự tuần tự về cách chúng được lưu trữ trong bộ nhớ nhanh hơn do vị trí bộ đệm (sử dụng tất cả các byte của dòng bộ đệm khi được tìm nạp), không phải do lỗi trang. (trừ khi bộ công việc của bạn quá lớn so với máy tính bạn đang sử dụng.)
Peter Cordes

10

Lần cuối cùng tôi bận tâm về nó là khi viết 6502 lắp ráp (8-bit, yeah!). Lợi ích lớn là hầu hết các phép toán số học (đặc biệt là giảm) đã cập nhật một bộ cờ, một trong số đó là Z, chỉ số 'đạt đến không'.

Vì vậy, ở cuối vòng lặp, bạn chỉ cần thực hiện hai hướng dẫn: DEC(giảm) và JNZ(nhảy nếu không bằng 0), không cần so sánh!


Trong trường hợp JavaScript rõ ràng là nó không áp dụng (vì nó chạy trên các CPU không có mã op như vậy). Rất có thể lý do thực sự đằng sau i--vs i++là vì trước đây bạn không đưa ra các biến kiểm soát bổ sung trong phạm vi của vòng lặp. Xem câu trả lời của tôi dưới đây ...
Pavel P

1
đúng, nó thực sự không áp dụng; nhưng đó là một kiểu chữ C rất phổ biến và nó có vẻ sạch sẽ hơn với những người trong chúng ta đã quen với điều đó. :-)
Javier

x86 và ARM đều giống như 6502 về mặt này. dec/jnzthay vì inc/cmp/jnecho trường hợp x86. Bạn sẽ không thấy một vòng lặp trống nào chạy nhanh hơn (cả hai sẽ bão hòa thông lượng nhánh), nhưng đếm ngược lại làm giảm nhẹ chi phí vòng lặp. Các trình tải trước Intel HW hiện tại không bị làm phiền bởi các mẫu truy cập bộ nhớ theo thứ tự giảm dần. Các CPU cũ hơn tôi nghĩ có thể theo dõi như 4 luồng ngược và 6 hoặc 10 luồng chuyển tiếp, IIRC.
Peter Cordes

7

Nó có thể được giải thích bằng JavaScript (và tất cả các ngôn ngữ) cuối cùng bị biến thành opcodes để chạy trên CPU. CPU luôn có một lệnh duy nhất để so sánh với 0, rất nhanh.

Bên cạnh đó, nếu bạn có thể đảm bảo countluôn luôn >= 0, bạn có thể đơn giản hóa để:

for (var i = count; i--;)
{
  // whatever
}

1
Mã nguồn ngắn hơn không nhất thiết có nghĩa là nó sẽ nhanh hơn. Bạn đã đo nó chưa?
Greg Hewgill

Ooops tôi đã bỏ lỡ cái này Nail trên đầu có chap.
Robert

1
Tôi muốn xem các nguồn lắp ráp trong đó so sánh với 0 là khác nhau. Đã được một vài năm, nhưng đã có lúc tôi thực hiện một tấn mã hóa lắp ráp và tôi không thể nghĩ ra cách so sánh / kiểm tra so với 0 theo cách không thể nhanh bằng cho bất kỳ số nguyên nào khác. Tuy nhiên, những gì bạn nói không đúng. Thất vọng vì tôi không thể hiểu tại sao!
Brian Knoblauch

7
@Brian Knoblauch: Nếu bạn sử dụng một lệnh như "dec eax" (mã x86) thì lệnh đó sẽ tự động đặt cờ Z (không) mà bạn có thể kiểm tra ngay lập tức mà không phải sử dụng một lệnh so sánh khác ở giữa.
Greg Hewgill

1
Khi giải thích Javascript, tôi nghi ngờ rằng opcodes sẽ là nút cổ chai. Nhiều khả năng là ít mã thông báo hơn có nghĩa là trình thông dịch có thể xử lý mã nguồn nhanh hơn.
Todd Owen

6

Điều này không phụ thuộc vào --hoặc ++ký, nhưng nó phụ thuộc vào các điều kiện bạn áp dụng trong vòng lặp.

Ví dụ: Vòng lặp của bạn nhanh hơn nếu biến có giá trị tĩnh so với khi vòng lặp của bạn kiểm tra các điều kiện mỗi lần, như độ dài của một mảng hoặc các điều kiện khác.

Nhưng đừng lo lắng về việc tối ưu hóa này, vì lần này hiệu quả của nó được đo bằng nano giây.


nhiều vòng lặp nano giây có thể trở thành giây ... không bao giờ là ý tưởng tồi để tối ưu hóa khi bạn có thời gian cho nó
Buksy

6

for(var i = array.length; i--; )không nhanh hơn nhiều Nhưng khi bạn thay thế array.lengthbằng super_puper_function(), điều đó có thể nhanh hơn đáng kể (vì nó được gọi trong mỗi lần lặp). Đó là sự khác biệt.

Nếu bạn sẽ thay đổi nó vào năm 2014, bạn không cần phải suy nghĩ về tối ưu hóa. Nếu bạn định thay đổi nó bằng "Tìm kiếm & Thay thế", bạn không cần phải suy nghĩ về tối ưu hóa. Nếu bạn không có thời gian, bạn không cần phải suy nghĩ về tối ưu hóa. Nhưng bây giờ, bạn đã có thời gian để suy nghĩ về nó.

PS: i--không nhanh hơn i++.


6

Để cắt ngắn: Hoàn toàn không có sự khác biệt trong việc làm điều này trong JavaScript.

Trước hết, bạn có thể tự kiểm tra nó:

Bạn không chỉ có thể kiểm tra và chạy bất kỳ tập lệnh nào trong bất kỳ thư viện JavaScript nào, mà bạn còn có quyền truy cập vào toàn bộ tập lệnh được viết trước đó, cũng như có lỗi để thấy sự khác biệt giữa thời gian thực hiện trong các trình duyệt khác nhau trên các nền tảng khác nhau.

Vì vậy, như bạn có thể thấy, không có sự khác biệt giữa hiệu suất trong bất kỳ môi trường nào.

Nếu bạn muốn cải thiện hiệu suất của tập lệnh của mình, những điều bạn có thể thử làm:

  1. Có một var a = array.length;tuyên bố để bạn sẽ không tính toán giá trị của nó mỗi lần trong vòng lặp
  2. Đừng hủy đăng ký http://en.wikipedia.org/wiki/Loop_unwinding

Nhưng bạn phải hiểu rằng sự cải thiện mà bạn có thể đạt được sẽ rất không đáng kể, mà hầu như bạn không nên quan tâm đến nó.

Ý kiến ​​riêng của tôi tại sao một quan niệm sai lầm như vậy (Dec vs Inc) xuất hiện

Cách đây rất lâu, đã có một hướng dẫn sử dụng máy phổ biến, DSZ (Decrement and Skip on Zero). Những người lập trình bằng ngôn ngữ lắp ráp đã sử dụng hướng dẫn này để thực hiện các vòng lặp để lưu một thanh ghi. Bây giờ sự thật cổ xưa này đã lỗi thời và tôi khá chắc chắn rằng bạn sẽ không nhận được bất kỳ cải tiến hiệu suất nào trong bất kỳ ngôn ngữ nào sử dụng cải tiến giả này.

Tôi nghĩ rằng cách duy nhất kiến ​​thức như vậy có thể truyền bá trong thời đại của chúng ta là khi bạn đọc mã người khác. Xem một cấu trúc như vậy và hỏi tại sao nó được thực hiện và đây là câu trả lời: "nó cải thiện hiệu suất vì nó so với không". Bạn trở nên hoang mang với kiến ​​thức cao hơn về đồng nghiệp của mình và nghĩ rằng nên sử dụng nó để thông minh hơn nhiều :-)


Thật thú vị, nhưng đối với các thử nghiệm của bạn chạy trong firefox 16.0.2 trên win7, vòng lặp giảm tốc chậm hơn 29% ... EDIT: Bỏ qua điều đó. Các thử nghiệm lặp đi lặp lại đã chứng minh không có kết luận, có một lượng nhiễu đáng ngạc nhiên trong kết quả thử nghiệm của tôi cho các lần chạy tiếp theo. Không hoàn toàn chắc chắn tại sao.

Vâng, tôi đã cố gắng giải thích điều đó bằng cách đóng mọi thứ khác lại và chỉ chạy các bài kiểm tra. Vẫn có kết quả thắng lợi. Rất lạ.

Tôi nghĩ rằng bạn đã bỏ lỡ điểm thực sự tại sao về số 0 được coi là tốt hơn trong JavaScript. Chủ yếu là vì cách này chỉ có một biến điều khiển thực thi vòng lặp, ví dụ như cách này trình tối ưu hóa / JITer có nhiều chỗ hơn để cải thiện. Việc sử dụng array.lengthkhông nhất thiết phải chịu hình phạt hiệu năng, đơn giản vì máy ảo JS đủ thông minh để tìm hiểu xem mảng không được sửa đổi bởi thân của vòng lặp. Xem câu trả lời của tôi dưới đây.
Pavel P

1
nitpicking: thực tế cổ xưa này (tối ưu hóa ngôn ngữ lắp ráp) không lỗi thời, chỉ là phức tạp. như trong bạn không cần phải biết trừ khi bạn thực sự làm. :-)
Javier

6

Tôi đã làm một so sánh trên jsbench .

Như alestani đã chỉ ra, một điều cần có thời gian trong các vòng tăng dần, là để đánh giá, với mỗi lần lặp, kích thước của mảng của bạn. Trong vòng lặp này:

for ( var i = 1; i <= array.length; i++ )

bạn đánh giá .lengthmỗi lần bạn tăng lên i. Trong cái này:

for ( var i = 1, l = array.length; i <= l; i++ )

bạn đánh giá .lengthchỉ một lần, khi bạn tuyên bố i. Trong cái này:

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

sự so sánh là ngầm định, nó xảy ra ngay trước khi giảm ivà mã rất dễ đọc. Tuy nhiên, những gì có thể tạo ra một sự khác biệt tuyệt vời, là những gì bạn đặt trong vòng lặp.

Lặp lại với chức năng gọi (được xác định ở nơi khác):

for (i = values.length; i-- ;) {
  add( values[i] );
}

Vòng lặp với mã nội tuyến:

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

Nếu bạn có thể nội tuyến mã của mình, thay vì gọi một hàm, mà không phải hy sinh mức độ dễ đọc, bạn có thể có một vòng lặp có độ lớn nhanh hơn!


Lưu ý : vì trình duyệt đang trở nên tốt trong việc nội tuyến các chức năng đơn giản, nó thực sự phụ thuộc vào mức độ phức tạp của mã của bạn. Vì vậy, hồ sơ trước khi tối ưu hóa, bởi vì

  1. Nút cổ chai có thể ở nơi khác (ajax, Reflow, ...)
  2. Bạn có thể chọn một thuật toán tốt hơn
  3. Bạn có thể chọn cấu trúc dữ liệu tốt hơn

Nhưng hãy nhớ:

Mã được viết để mọi người đọc và chỉ tình cờ cho máy thực thi.


+1 cho câu trả lời này và cho điểm chuẩn. Tôi đã thêm các bài kiểm tra forEach và làm lại điểm chuẩn cho một tệp độc lập có thể chạy được trong trình duyệt cũng như trong Node. jsfiddle.net/hta69may/2 . Đối với Node "Vòng lặp ngược, so sánh ngầm, mã nội tuyến" là nhanh nhất. Nhưng thử nghiệm trong FF 50 cho thấy kết quả gây tò mò: không chỉ thời gian ít hơn gần 10 lần (!) Mà cả hai thử nghiệm "forEach" đều nhanh như "vòng lặp ngược". Có lẽ các chàng trai Node nên sử dụng công cụ JS của Mozilla thay vì V8? :)
Fr0sT

4

Cách bạn đang làm bây giờ không nhanh hơn (ngoài việc nó là một vòng lặp không xác định, tôi đoán bạn có nghĩa là phải làm i--.

Nếu bạn muốn làm cho nó nhanh hơn, hãy làm:

for (i = 10; i--;) {
    //super fast loop
}

tất nhiên bạn sẽ không nhận thấy nó trên một vòng lặp nhỏ như vậy. Lý do nhanh hơn là vì bạn đang giảm tôi trong khi kiểm tra xem nó có "đúng" không (nó đánh giá là "sai" khi nó về 0)


1
Bạn đang thiếu một dấu chấm phẩy? (i = 10; i--;)
searlea

Có, tôi đã sửa lỗi. Và nếu chúng ta sẽ kén chọn, tôi sẽ chỉ ra rằng bạn đã quên dấu chấm phẩy sau i--! Heh.
djdd87

Tại sao điều đó sẽ nhanh hơn? Nguồn ngắn hơn không có nghĩa là nó sẽ nhanh hơn. Bạn đã đo nó chưa?
Greg Hewgill

4
Vâng, tôi đã đo nó và tôi không nói nguồn ngắn hơn làm cho nó nhanh hơn, nhưng ít thao tác hơn làm cho nó nhanh hơn.
peirix

4

Đôi khi thực hiện một số thay đổi rất nhỏ đối với cách chúng tôi viết mã của mình có thể tạo ra sự khác biệt lớn đối với việc mã của chúng tôi thực sự chạy nhanh như thế nào. Một lĩnh vực mà một thay đổi mã nhỏ có thể tạo ra sự khác biệt lớn đối với thời gian thực hiện là nơi chúng ta có một vòng lặp for đang xử lý một mảng. Trong đó mảng là các phần tử trên trang web (chẳng hạn như các nút radio), thay đổi có tác dụng lớn nhất nhưng vẫn đáng để áp dụng thay đổi này ngay cả khi mảng nằm trong mã Javascript.

Cách thông thường để mã hóa một vòng lặp for để xử lý một mảng như thế này:

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

Vấn đề với điều này là việc đánh giá độ dài của mảng bằng myArray.length cần có thời gian và cách chúng ta đã mã hóa vòng lặp có nghĩa là việc đánh giá này phải được thực hiện mỗi lần xung quanh vòng lặp. Nếu mảng chứa 1000 phần tử thì độ dài của mảng sẽ được ước tính 1001 lần. Nếu chúng ta đang xem các nút radio và có myForm.myButtons.length thì sẽ mất nhiều thời gian hơn để đánh giá vì nhóm nút thích hợp trong biểu mẫu đã chỉ định phải được định vị trước khi có thể đánh giá độ dài mỗi lần xung quanh vòng lặp.

Rõ ràng chúng ta không mong đợi độ dài của mảng sẽ thay đổi trong khi chúng ta đang xử lý nó, vì vậy tất cả các phép tính lại độ dài này chỉ là thêm một cách không cần thiết vào thời gian xử lý. (Tất nhiên nếu bạn có mã bên trong vòng lặp thêm hoặc xóa các mục mảng thì kích thước mảng có thể thay đổi giữa các lần lặp và vì vậy chúng tôi không thể thay đổi mã kiểm tra cho nó)

Những gì chúng ta có thể làm để sửa lỗi này cho một vòng lặp trong đó kích thước được cố định là đánh giá độ dài một lần khi bắt đầu vòng lặp và lưu nó vào một biến. Sau đó chúng ta có thể kiểm tra biến để quyết định khi nào chấm dứt vòng lặp. Điều này nhanh hơn nhiều so với việc đánh giá độ dài mảng mỗi lần, đặc biệt khi mảng chứa nhiều hơn chỉ một vài mục hoặc là một phần của trang web.

Mã để làm điều này là:

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

Vì vậy, bây giờ chúng tôi chỉ đánh giá kích thước của mảng một lần và kiểm tra bộ đếm vòng lặp của chúng tôi dựa vào biến giữ giá trị đó mỗi lần xung quanh vòng lặp. Biến bổ sung này có thể được truy cập nhanh hơn nhiều so với việc đánh giá kích thước của mảng và do đó mã của chúng tôi sẽ chạy nhanh hơn nhiều so với trước đây. Chúng tôi chỉ có một biến thêm trong kịch bản của chúng tôi.

Thông thường, chúng tôi không xử lý mảng theo thứ tự nào miễn là tất cả các mục trong mảng được xử lý. Trong trường hợp này, chúng ta có thể làm cho mã của mình nhanh hơn một chút bằng cách loại bỏ biến phụ mà chúng ta vừa thêm và xử lý mảng theo thứ tự ngược lại.

Mã cuối cùng xử lý mảng của chúng tôi theo cách hiệu quả nhất có thể là:

for (var i = myArray.length-1; i > -1; i--) {...

Mã này vẫn chỉ đánh giá kích thước của mảng một lần khi bắt đầu nhưng thay vì so sánh bộ đếm vòng lặp với một biến, chúng ta so sánh nó với một hằng số. Vì một hằng số thậm chí còn hiệu quả hơn để truy cập so với một biến và vì chúng ta có một câu lệnh gán ít hơn trước phiên bản thứ ba của mã bây giờ hiệu quả hơn một chút so với phiên bản thứ hai và hiệu quả hơn nhiều so với phiên bản thứ nhất.


4

++vs. --không quan trọng vì JavaScript là ngôn ngữ được dịch, không phải là ngôn ngữ được biên dịch. Mỗi hướng dẫn dịch sang nhiều ngôn ngữ máy và bạn không nên quan tâm đến các chi tiết chính.

Những người đang nói về việc sử dụng --(hoặc ++) để sử dụng hiệu quả các hướng dẫn lắp ráp là sai. Các hướng dẫn này áp dụng cho số học số nguyên và không có số nguyên trong JavaScript, chỉ có số .

Bạn nên viết mã có thể đọc được.


3

Trong nhiều trường hợp, điều này về cơ bản không liên quan gì đến thực tế là bộ xử lý có thể so sánh với 0 nhanh hơn so với các so sánh khác.

Điều này là do chỉ một số công cụ Javascript (những công cụ trong danh sách JIT) thực sự tạo mã ngôn ngữ máy.

Hầu hết các công cụ Javascript xây dựng một biểu diễn bên trong của mã nguồn mà sau đó chúng diễn giải (để có ý tưởng về việc này giống như thế nào, hãy nhìn gần cuối trang này trên SpiderMonkey của Firefox ). Nói chung nếu một đoạn mã thực tế giống như vậy nhưng dẫn đến biểu diễn bên trong đơn giản hơn, nó sẽ chạy nhanh hơn.

Hãy nhớ rằng với các tác vụ đơn giản như cộng / trừ một biến từ một biến hoặc so sánh một biến với một thứ gì đó, thì chi phí của trình thông dịch chuyển từ một "lệnh" nội bộ sang tiếp theo là khá cao, do đó, ít "lệnh" hơn được sử dụng nội bộ bởi công cụ JS, thì tốt hơn.


3

Chà, tôi không biết về JavaScript, nó thực sự chỉ là vấn đề đánh giá lại độ dài mảng và có thể phải làm gì đó với các mảng kết hợp (nếu bạn chỉ giảm, thì các mục mới không chắc sẽ được phân bổ - nếu mảng dày đặc, nghĩa là ai đó có thể tối ưu hóa cho điều đó).

Trong lắp ráp cấp thấp, có một lệnh lặp, được gọi là DJNZ (giảm và nhảy nếu khác không). Vì vậy, giảm và nhảy là tất cả trong một hướng dẫn, làm cho nó có thể nhanh hơn một chút so với INC và JL / JB (tăng, nhảy nếu nhỏ hơn / nhảy nếu bên dưới). Ngoài ra, so sánh với số 0 đơn giản hơn so với so với số khác. Nhưng tất cả những điều đó thực sự rất ít và cũng phụ thuộc vào kiến ​​trúc mục tiêu (có thể tạo ra sự khác biệt, ví dụ như trên Arm trong điện thoại thông minh).

Tôi không mong đợi sự khác biệt ở cấp độ thấp này có tác động lớn đến các ngôn ngữ được giải thích, tôi chỉ không thấy DJNZ trong số các phản hồi nên tôi nghĩ tôi sẽ chia sẻ một suy nghĩ thú vị.


Đối với bản ghi, DJNZlà một hướng dẫn trong ISA 8051 (z80). x86 đã dec/jnzthay thế inc/cmp/jne, và rõ ràng cánh tay có một cái gì đó tương tự. Tôi khá chắc chắn rằng đây không phải là nguyên nhân của sự khác biệt Javascript; đó là từ có nhiều hơn để đánh giá trong điều kiện vòng lặp.
Peter Cordes

3

Người ta thường nói rằng --i nhanh hơn (trong C ++) vì chỉ có một kết quả, giá trị giảm dần. i-- cần lưu trữ giá trị giảm dần về i và cũng giữ lại giá trị ban đầu là kết quả (j = i--;). Trong hầu hết các trình biên dịch, điều này đã sử dụng hết hai thanh ghi thay vì một thanh ghi có thể khiến một biến khác phải được ghi vào bộ nhớ thay vì được giữ lại làm biến đăng ký.

Tôi đồng ý với những người khác đã nói rằng nó làm cho không có sự khác biệt những ngày này.


3

Nói một cách rất đơn giản

"i-- và i ++. Thật ra, cả hai đều mất cùng một lúc".

nhưng trong trường hợp này khi bạn có thao tác tăng dần .. bộ xử lý đánh giá .length mỗi biến thời gian được tăng thêm 1 và trong trường hợp giảm dần .. đặc biệt trong trường hợp này, nó sẽ chỉ đánh giá .length một lần cho đến khi chúng tôi nhận được 0.


3

Đầu tiên, i++i-- dành chính xác cùng thời gian cho bất kỳ ngôn ngữ lập trình nào, bao gồm cả JavaScript.

Các mã sau đây mất nhiều thời gian khác nhau.

Nhanh:

for (var i = 0, len = Things.length - 1; i <= len; i++) { Things[i] };

Chậm:

for (var i = 0; i <= Things.length - 1; i++) { Things[i] };

Do đó các mã sau đây mất thời gian khác nhau quá.

Nhanh:

for (var i = Things.length - 1; i >= 0; i--) { Things[i] };

Chậm:

for (var i = 0; i <= Things.length - 1; i++) { Things[i] };

PS Slow chỉ chậm đối với một số ngôn ngữ (công cụ JavaScript) do tối ưu hóa của trình biên dịch. Cách tốt nhất là sử dụng '<' thay vì '<=' (hoặc '=') và '--i' thay vì 'i--' .


3

Không có nhiều thời gian được sử dụng bởi i-- hoặc i ++. Nếu bạn đi sâu vào bên trong kiến ​​trúc CPU ++thì tốc độ nhanh hơn so với --, vì --hoạt động sẽ thực hiện bổ sung cho 2, nhưng nó sẽ hoạt động bên trong phần cứng nên điều này sẽ làm cho nó nhanh hơn và không có sự khác biệt lớn nào giữa ++--các hoạt động này được coi là tiêu tốn ít thời gian nhất trong CPU.

Các vòng lặp for chạy như thế này:

  • Khởi tạo biến một lần khi bắt đầu.
  • Kiểm tra các hạn chế trong các toán hạng thứ hai của vòng lặp, <, >, <=vv
  • Sau đó áp dụng vòng lặp.
  • Tăng vòng lặp và vòng lặp một lần nữa ném các quá trình này một lần nữa.

Vì thế,

for (var i = Things.length - 1; i >= 0; i--) {
    Things[i]
}; 

sẽ tính toán độ dài mảng chỉ một lần khi bắt đầu và đây không phải là nhiều thời gian, nhưng

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

sẽ tính toán độ dài ở mỗi vòng lặp, vì vậy nó sẽ tiêu tốn rất nhiều thời gian.


var i = Things.length - 1; i >= 0; i--sẽ tính chiều dài 1 lần nữa.
Dmitry

1
Tôi không chắc " --hoạt động sẽ làm bổ sung cho 2" nghĩa là gì, nhưng tôi cho rằng điều đó có nghĩa là nó sẽ phủ nhận điều gì đó. Không, nó không phủ nhận bất cứ điều gì trên bất kỳ kiến ​​trúc. Trừ đi 1 chỉ đơn giản như thêm 1, bạn chỉ cần tạo một mạch mượn chứ không phải mang.
Oledit

1

Cách tiếp cận tốt nhất để trả lời loại câu hỏi này là thực sự thử nó. Thiết lập một vòng lặp đếm một triệu lần lặp hoặc bất cứ điều gì và thực hiện cả hai cách. Thời gian cả hai vòng, và so sánh kết quả.

Câu trả lời có thể sẽ phụ thuộc vào trình duyệt bạn đang sử dụng. Một số sẽ có kết quả khác so với những người khác.


4
Điều đó vẫn không trả lời câu hỏi của anh ấy về lý do tại sao nó nhanh hơn. Anh ta chỉ đạt được một điểm chuẩn, mà không có bất kỳ kiến ​​thức nào về lý do tại sao lại như vậy ...
peirix

3
Đúng. Tuy nhiên, không biết triển khai chính xác từng công cụ Javascript trong mỗi trình duyệt, gần như không thể trả lời phần "tại sao". Rất nhiều câu trả lời ở đây xoay quanh các khuyến nghị giai thoại như "sử dụng tiền xử lý thay vì hậu xử lý" (một mẹo tối ưu hóa C ++) và "so sánh với không" có thể đúng trong các ngôn ngữ được biên dịch mã gốc nhưng Javascript khá xa so với kim loại trần của CPU.
Greg Hewgill

4
-1 Tôi hoàn toàn không đồng ý rằng cách tiếp cận tốt nhất là thử nó. Một vài ví dụ không thay thế kiến ​​thức tập thể, và đó là toàn bộ quan điểm của các diễn đàn như thế này.
Barshe

1

Yêu nó, rất nhiều đánh dấu lên nhưng không có câu trả lời: D

Đơn giản chỉ cần đặt một so sánh với số không luôn là so sánh nhanh nhất

Vì vậy (a == 0) thực sự nhanh hơn khi trả về True hơn (a == 5)

Nó nhỏ và không đáng kể và với 100 triệu hàng trong một bộ sưu tập, nó có thể đo lường được.

tức là trên một vòng lặp lên, bạn có thể nói rằng tôi <= Array.length và đang tăng i

trên một vòng lặp xuống, bạn có thể nói rằng i> = 0 và đang giảm dần thay vào đó.

Việc so sánh nhanh hơn. Không phải là "hướng" của vòng lặp.


Không có câu trả lời cho câu hỏi như đã nêu vì các công cụ Javascript đều khác nhau và câu trả lời phụ thuộc vào chính xác trình duyệt mà bạn đang đo.
Greg Hewgill

Không, đó là so sánh cơ bản được chấp nhận so với không là nhanh nhất. Mặc dù tuyên bố của bạn là chính xác cũng như quy tắc vàng so sánh với không là tuyệt đối.
Robert

4
Điều đó chỉ đúng nếu trình biên dịch chọn thực hiện tối ưu hóa đó, điều này chắc chắn không được đảm bảo. Trình biên dịch có thể tạo chính xác cùng một mã cho (a == 0) và (a == 5), ngoại trừ giá trị của hằng. CPU sẽ không so sánh với 0 nhanh hơn bất kỳ giá trị nào khác, nếu cả hai mặt so sánh là các biến (theo quan điểm của CPU). Nói chung chỉ có trình biên dịch mã gốc mới có cơ hội tối ưu hóa ở cấp độ này.
Greg Hewgill

1

GIÚP NGƯỜI KHÁC TRÁNH MỘT TRỤ SỞ --- BẦU CỬ NÀY !!!

Câu trả lời phổ biến nhất trên trang này không hoạt động cho Firefox 14 và không vượt qua jsLinter. Các vòng lặp "trong khi" cần một toán tử so sánh, không phải là phép gán. Nó không hoạt động trên chrome, safari, và thậm chí tức là. Nhưng chết trong firefox.

NÓ BỊ HỎNG!

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

NÀY S WOR LÀM VIỆC! (hoạt động trên firefox, vượt qua jsLinter)

var i = arr.length; //or 10
while(i>-1)
{
  //...
  i = i - 1;
}

2
Tôi mới thử nó trên Firefox 14 và nó hoạt động tốt. Tôi đã thấy các ví dụ từ các thư viện sản xuất sử dụng while (i--)và hoạt động trên nhiều trình duyệt. Có thể có điều gì đó kỳ lạ về bài kiểm tra của bạn? Bạn có đang sử dụng phiên bản beta của Firefox 14 không?
Matt Browne

1

Đây chỉ là dự đoán, nhưng có lẽ vì bộ xử lý dễ dàng so sánh thứ gì đó với 0 (i> = 0) thay vì với giá trị khác (i <Things.length).


3
+1 Đừng để người khác bỏ phiếu. Mặc dù việc đánh giá lặp đi lặp lại của .length là một vấn đề nhiều hơn so với việc giảm / giảm thực tế, việc kiểm tra kết thúc vòng lặp có thể quan trọng. Trình biên dịch C của tôi đưa ra một số cảnh báo về các vòng lặp như: "nhận xét # 1544-D: (ULP 13.1) Đã phát hiện vòng lặp đếm ngược. Đề xuất các vòng lặp đếm ngược khi phát hiện số không dễ dàng hơn"
harper
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.