“IF” có đắt không?


98

Tôi không thể nhớ chính xác những gì giáo viên của chúng tôi đã nói ngày hôm đó và tôi hy vọng bạn có thể sẽ biết.

Mô-đun là "Cấu trúc dữ liệu và thuật toán" và anh ấy đã nói với chúng tôi điều gì đó dọc theo dòng:

Các iftuyên bố là nhất đắt [gì đó]. [cái gì đó] đăng ký [cái gì đó].

Vâng, tôi thực sự có một ký ức kinh hoàng và tôi thực sự xin lỗi, nhưng tôi đã tìm kiếm trên Google trong nhiều giờ và không có kết quả gì. Bất kỳ ý tưởng?


29
Hỏi giáo viên của bạn có phải là một lựa chọn?
Michael Myers

7
Tại sao bạn không gửi email cho giáo viên của bạn? Không có ai trên SO biết giáo viên của bạn nói gì, trừ khi họ ở đó vào thời điểm đó (hoặc chính giáo viên của bạn đọc SO).
Bill Karwin

11
Và tất nhiên một liên kết đến bắt buộc railroad câu trả lời
bobobobo

Các câu lệnh if hoặc đặc biệt là các biểu thức "?:" Trong ngôn ngữ ngoặc nhọn chịu ảnh hưởng của C có thể được thực hiện bằng các lệnh thực thi có điều kiện đặc biệt trên bộ xử lý x86 và nhánh. Đây là những hướng dẫn thực hiện hoặc không thực hiện một số thao tác dựa trên một thử nghiệm trước. Sử dụng các hướng dẫn tuyệt vời này sẽ tránh hoàn toàn nhu cầu về các lệnh nhảy / nhánh / 'goto' có điều kiện. Một sự cải thiện hiệu suất rất lớn trong một số tình huống bằng cách làm cho dòng chương trình hoàn toàn có thể dự đoán được vì nó chỉ chạy thẳng mà không (có thể không đoán trước được) nhảy xung quanh các điểm khác nhau trong mã.
Cecil Ward

Một trình biên dịch tốt đôi khi có thể cần một chút thúc đẩy theo đúng hướng để nó sử dụng các lệnh có điều kiện thay vì ngu ngốc và sử dụng các bước nhảy có điều kiện, bằng cách tổ chức lại mã và có thể sử dụng một số học thông minh trong một biểu thức hoặc một? : biểu hiện. Đừng chơi với điều này trừ khi bạn thực sự biết asm của mình và đã đọc các hướng dẫn tối ưu hóa ví dụ như Agner Fog. Các trình biên dịch đôi khi làm đúng bất kể câu lệnh if hay? : biểu thức được sử dụng.
Cecil Ward

Câu trả lời:


185

Ở mức thấp nhất (trong phần cứng), có, nếu s là đắt. Để hiểu tại sao, bạn phải hiểu cách thức hoạt động của đường ống .

Lệnh hiện tại được thực thi được lưu trữ trong một thứ thường được gọi là con trỏ lệnh (IP) hoặc bộ đếm chương trình (PC); các thuật ngữ này đồng nghĩa, nhưng các thuật ngữ khác nhau được sử dụng với các kiến ​​trúc khác nhau. Đối với hầu hết các lệnh, PC của lệnh tiếp theo chỉ là PC hiện tại cộng với độ dài của lệnh hiện tại. Đối với hầu hết các kiến ​​trúc RISC, các lệnh đều có độ dài không đổi, vì vậy PC có thể được tăng thêm một lượng không đổi. Đối với kiến ​​trúc CISC chẳng hạn như x86, các lệnh có thể có độ dài thay đổi, do đó logic giải mã lệnh phải tìm ra lệnh hiện tại là bao lâu để tìm vị trí của lệnh tiếp theo.

Tuy nhiên, đối với các lệnh rẽ nhánh , lệnh tiếp theo được thực thi không phải là vị trí tiếp theo sau lệnh hiện tại. Các nhánh là gotos - chúng cho bộ xử lý biết lệnh tiếp theo ở đâu. Các nhánh có thể có điều kiện hoặc không có điều kiện và vị trí đích có thể cố định hoặc được tính toán.

Có điều kiện so với không điều kiện rất dễ hiểu - một nhánh có điều kiện chỉ được sử dụng nếu một điều kiện nhất định được giữ nguyên (chẳng hạn như một số có bằng một số khác); nếu nhánh không được thực hiện, điều khiển sẽ chuyển sang lệnh tiếp theo sau nhánh như bình thường. Đối với cành không điều kiện, cành luôn được lấy. Các nhánh có điều kiện hiển thị trong các ifcâu lệnh và các thử nghiệm điều khiển của forwhilevòng lặp. Các nhánh không điều kiện hiển thị trong các vòng lặp vô hạn, lệnh gọi hàm, trả về hàm breakcontinuecâu lệnh, gotocâu lệnh khét tiếng , và nhiều thứ khác (những danh sách này chưa đầy đủ).

Mục tiêu chi nhánh là một vấn đề quan trọng khác. Hầu hết các nhánh đều có mục tiêu nhánh cố định - chúng đi đến một vị trí cụ thể trong mã được cố định tại thời điểm biên dịch. Điều này bao gồm các ifcâu lệnh, các vòng lặp thuộc tất cả các loại, các lệnh gọi hàm thông thường và nhiều thứ khác. Các nhánh được tính toán sẽ tính toán mục tiêu của nhánh trong thời gian chạy. Điều này bao gồm các switchcâu lệnh (đôi khi), trả về từ một hàm, các lệnh gọi hàm ảo và các lệnh gọi con trỏ hàm.

Vậy tất cả những điều này có ý nghĩa gì đối với hiệu suất? Khi bộ xử lý thấy một lệnh rẽ nhánh xuất hiện trong đường ống của nó, nó cần tìm cách tiếp tục lấp đầy đường ống của nó. Để tìm ra hướng dẫn nào xuất hiện sau nhánh trong luồng chương trình, cần biết hai điều: (1) nếu nhánh sẽ được thực hiện và (2) mục tiêu của nhánh. Việc tìm ra điều này được gọi là dự đoán nhánh và đó là một vấn đề đầy thách thức. Nếu bộ xử lý đoán đúng, chương trình sẽ tiếp tục ở tốc độ tối đa. Thay vào đó, nếu bộ xử lý đoán sai , nó chỉ dành một chút thời gian để tính toán sai. Bây giờ nó phải xả đường ống của nó và tải lại nó với các hướng dẫn từ đường dẫn thực thi chính xác. Điểm mấu chốt: một thành công lớn về hiệu suất.

Vì vậy, lý do tại sao các câu lệnh if lại đắt là do sự sai lệch của nhánh . Đây chỉ là mức thấp nhất. Nếu bạn đang viết mã cấp cao, bạn không cần phải lo lắng về những chi tiết này. Bạn chỉ nên quan tâm đến điều này nếu bạn đang viết mã cực kỳ quan trọng về hiệu suất trong C hoặc assembly. Trong trường hợp đó, viết mã không nhánh thường có thể vượt trội hơn mã nhánh, ngay cả khi cần thêm một số hướng dẫn. Có một số thủ đoạn mát-bit twiddling bạn có thể làm để tính toán những thứ như abs(), min()max()không phân nhánh.


20
Nó không chỉ là sai lầm của chi nhánh. Các nhánh cũng ngăn chặn việc sắp xếp lại thứ tự lệnh, ở cấp trình biên dịch và ở một mức độ nào đó ở cấp CPU (tất nhiên là đối với CPU không theo thứ tự). Tuy nhiên, câu trả lời rất chi tiết.
jalf

5
Nếu các ngôn ngữ cấp cao cuối cùng được dịch xuống các ngôn ngữ cấp thấp và bạn đang viết mã tập trung vào hiệu suất, bạn vẫn không thu được gì bằng cách viết mã tránh câu lệnh if? Liệu khái niệm này không áp dụng cho các ngôn ngữ cấp cao hơn?
c ..

18

"Đắt" là một thuật ngữ rất tương đối, đặc biệt là liên quan đến ifcâu lệnh "" vì bạn cũng phải tính đến chi phí của điều kiện. Điều đó có thể bao gồm bất cứ nơi nào từ một vài hướng dẫn cpu ngắn cho đến kiểm tra kết quả của một hàm gọi ra cơ sở dữ liệu từ xa.

Tôi không lo lắng về điều đó. Trừ khi bạn đang làm lập trình nhúng, bạn có thể không nên lo lắng về chi phí của " if". Đối với hầu hết các lập trình viên, nó sẽ không bao giờ là yếu tố thúc đẩy hiệu suất ứng dụng của bạn.


1
Chắc chắn là tương đối ... cmp / cond jmp vẫn nhanh hơn mul trên nhiều bộ xử lý.
Brian Knoblauch

4
Vâng, tôi đồng ý rằng tôi không nên lo lắng về điều đó. Tôi không cố gắng tối ưu hóa bất cứ thứ gì ở đây. Tôi chỉ đang cố gắng tìm hiểu và học hỏi. ;)
pek

15

Các nhánh, đặc biệt là trên bộ vi xử lý kiến ​​trúc RISC, là một số lệnh đắt nhất. Điều này là do trên nhiều kiến ​​trúc, trình biên dịch dự đoán đường dẫn thực thi nào sẽ được thực hiện nhiều nhất và đặt các hướng dẫn đó tiếp theo trong tệp thực thi, vì vậy chúng sẽ ở trong bộ nhớ cache của CPU khi rẽ nhánh xảy ra. Nếu nhánh đi theo hướng khác, nó phải quay trở lại bộ nhớ chính và tìm nạp các hướng dẫn mới - điều đó khá tốn kém. Trên nhiều kiến ​​trúc RISC, tất cả các lệnh đều là một chu kỳ ngoại trừ nhánh (thường là 2 chu kỳ). Chúng tôi không nói về một chi phí lớn ở đây, vì vậy đừng lo lắng về nó. Ngoài ra, trình biên dịch sẽ tối ưu hóa tốt hơn bạn làm 99% thời gian: ) Một trong những điều thực sự tuyệt vời về kiến ​​trúc EPIC (Itanium là một ví dụ) là nó lưu trữ (và bắt đầu xử lý) các lệnh từ cả hai phía của nhánh, sau đó loại bỏ tập mà nó không cần sau khi kết quả của nhánh là đã biết. Điều này giúp tiết kiệm truy cập bộ nhớ bổ sung của một kiến ​​trúc điển hình trong trường hợp nó phân nhánh dọc theo đường dẫn không được bảo vệ.


13

Kiểm tra bài viết Hiệu suất Tốt hơn Thông qua Loại bỏ Nhánh trên Hiệu suất Tế bào. Một bài thú vị khác là bài đăng này về các lựa chọn không phân nhánh trên Blog phát hiện va chạm theo thời gian thực.

Ngoài các câu trả lời tuyệt vời đã được đăng để trả lời cho câu hỏi này, tôi muốn lưu ý rằng mặc dù các câu lệnh "if" được coi là các phép toán cấp thấp đắt tiền, cố gắng sử dụng các kỹ thuật lập trình không nhánh trong môi trường cấp cao hơn , chẳng hạn như ngôn ngữ kịch bản hoặc lớp logic nghiệp vụ (bất kể ngôn ngữ), có thể không phù hợp một cách kỳ cục.

Phần lớn thời gian, các chương trình nên được viết cho rõ ràng trước tiên và tối ưu hóa hiệu suất thứ hai. Có rất nhiều lĩnh vực vấn đề mà hiệu suất là tối quan trọng, nhưng thực tế đơn giản là hầu hết các nhà phát triển không viết các mô-đun để sử dụng sâu trong lõi của công cụ kết xuất hoặc mô phỏng động lực học chất lỏng hiệu suất cao chạy trong nhiều tuần liên tục. Khi ưu tiên hàng đầu là giải pháp của bạn để "chỉ hoạt động", điều cuối cùng trong đầu bạn nên là liệu bạn có thể tiết kiệm chi phí của một câu lệnh điều kiện trong mã của bạn hay không.


Thật! Người ta cũng có thể nói thêm rằng, khi viết mã bằng ngôn ngữ khuyến khích các cuộc gọi (về cơ bản, bất kỳ thứ gì khác ngoài trình hợp dịch hoặc C không có stdlib), sự can thiệp của đường ống từ các kỹ thuật lập trình thông thường sẽ lấn át mọi câu hỏi về phân nhánh có điều kiện.
Ross Patterson

10

iftự nó không phải là chậm. Sự chậm chạp luôn là tương đối, tôi dám cá với cuộc đời mình rằng bạn chưa bao giờ cảm thấy "ngốn" của câu lệnh if. Nếu bạn định tạo một mã hiệu suất cao, bạn chắc chắn muốn tránh các nhánh. Điều làm cho ifchậm là bộ xử lý đang tải trước mã từ sau ifdựa trên một số heuristic và whatnot. Nó cũng sẽ ngăn đường ống thực thi mã trực tiếp sau lệnh ifrẽ nhánh trong mã máy, vì bộ xử lý chưa biết đường dẫn nào sẽ được thực hiện (trong bộ xử lý đường ống, nhiều lệnh được xen kẽ và thực thi). Mã được thực thi có thể phải được thực thi ngược lại (nếu nhánh khác được sử dụng. Nó được gọi là branch misprediction), hoặc noopphải được điền vào những nơi đó để điều này không xảy ra.

Nếu iflà ác, thì switchcũng là ác, và &&, ||cũng vậy. Đừng lo lắng về nó.


7

Ở cấp độ thấp nhất có thể ifbao gồm (sau khi tính toán tất cả các điều kiện tiên quyết cụ thể cho ứng dụng cụ thể if):

  • một số hướng dẫn kiểm tra
  • nhảy đến một số vị trí trong mã nếu kiểm tra thành công, hãy tiếp tục chuyển tiếp.

Chi phí liên quan đến điều đó:

  • so sánh cấp thấp - thường là hoạt động 1 cpu, siêu rẻ
  • bước nhảy tiềm năng - có thể tốn kém

Giải thích lý do tại sao các bước nhảy đắt tiền:

  • bạn có thể chuyển đến mã arbirary nằm ở bất kỳ đâu trong bộ nhớ, nếu nó không được cpu lưu vào bộ nhớ cache - chúng tôi gặp sự cố, vì chúng tôi cần truy cập bộ nhớ chính, bộ nhớ này chậm hơn
  • các CPU hiện đại thực hiện điều hướng nhánh. Họ cố gắng đoán xem liệu có thành công hay không và thực thi mã trước trong quá trình, vì vậy hãy tăng tốc mọi thứ. Nếu dự đoán không thành công, tất cả các tính toán được thực hiện trước theo đường ống phải bị vô hiệu. Đó cũng là một hoạt động tốn kém

Vì vậy, tóm lại:

  • Nếu có thể được nhanh chóng, nếu bạn thực sự, thực sự, tương đối quan tâm đến hiệu suất.
  • Bạn nên quan tâm đến nó nếu và chỉ khi bạn đang viết raytracer thời gian thực hoặc mô phỏng sinh học hoặc thứ gì đó tương tự. Không có lý do gì để quan tâm đến nó trong hầu hết thế giới thực.

Hãy đưa điều này lên cấp độ tiếp theo: còn câu lệnh if lồng nhau và / hoặc ghép thì sao? Chi phí có thể trở nên khá nhanh chóng nếu ai đó viết nhiều câu lệnh if như thế này. Và vì đối với hầu hết các nhà phát triển nếu các câu lệnh có vẻ giống như một hoạt động cơ bản như vậy, thì việc tránh phân nhánh có điều kiện phức tạp thường được coi là một mối quan tâm theo kiểu. Mối quan tâm về phong cách vẫn quan trọng, nhưng thường trong thời điểm nóng nực, chúng có thể là mối quan tâm đầu tiên bị bỏ qua.
jaydel

7

Các bộ xử lý hiện đại có đường ống thực thi dài có nghĩa là một số lệnh được thực hiện trong các giai đoạn khác nhau cùng một lúc. Họ có thể không phải lúc nào cũng biết kết quả của một lệnh khi lệnh tiếp theo bắt đầu chạy. Khi họ gặp phải một bước nhảy có điều kiện (nếu), đôi khi họ phải đợi cho đến khi đường dẫn trống trước khi họ có thể biết con trỏ lệnh sẽ đi theo hướng nào.

Tôi nghĩ về nó như một chuyến tàu chở hàng dài. Nó có thể chở rất nhiều hàng hóa nhanh chóng trên một đường thẳng, nhưng nó nghiêng góc xấu.

Pentium 4 (Prescott) có một đường ống dài nổi tiếng gồm 31 giai đoạn.

Thêm trên Wikipedia


3
+1 cho phép ẩn dụ về tàu chở hàng - Tôi sẽ nhớ rằng lần sau tôi cần giải thích các đường ống xử lý.
Daniel Pryden,

6

Có thể việc phân nhánh giết chết việc tìm nạp trước lệnh CPU?


Sau khi ... "nghiên cứu" của tôi, tôi đã học về bảng nhảy và phân nhánh cho các câu lệnh switch nhưng không biết gì về câu lệnh if. Bạn có thể giải thích một chút về điều đó?
pek

IIRC, CPU thường tìm nạp trước các lệnh dọc theo một đường dẫn thực thi có thể xảy ra duy nhất, nhưng câu lệnh 'if' gây ra một nhánh từ đường thực thi được dự đoán, nó sẽ làm mất hiệu lực của các lệnh tìm nạp trước và quá trình xử lý trước sẽ phải khởi động lại.
activout.

Bất kỳ bộ xử lý tốt nào cũng phải có khả năng dự đoán nhánh sẽ cố gắng đoán xem một nhánh sẽ được sử dụng hay không và tìm nạp trước hướng dẫn dựa trên dự đoán (nói chung là khá tốt). GCC thậm chí còn có phần mở rộng C cho phép lập trình viên cung cấp gợi ý cho các dự báo nhánh.
mipadi

2
Hơn nữa, CPU thường nhìn về phía trước để bắt đầu thực hiện các lệnh sắp tới sớm (không chỉ tìm nạp trước chúng) và trình biên dịch cố gắng sắp xếp lại các lệnh và điều đó trở nên nguy hiểm trên các nhánh, vì vậy bạn thực sự có thể hủy bỏ việc lập lịch lệnh với quá nhiều nhánh. Điều đó làm ảnh hưởng đến hiệu suất.
jalf

6

Cũng lưu ý rằng bên trong một vòng lặp không nhất thiết phải rất đắt tiền.

CPU hiện đại giả định khi lần đầu tiên truy cập câu lệnh if, rằng "if-body" sẽ được sử dụng (hay nói cách khác: nó cũng giả định một vòng lặp được thực hiện nhiều lần) (*). Sau lần truy cập thứ hai và các lần tiếp theo, nó (CPU) có thể nhìn vào Bảng Lịch sử Nhánh và xem tình trạng của lần trước như thế nào (nó đúng không? Nó sai?). Nếu lần trước nó là false, thì quá trình thực thi suy đoán sẽ tiếp tục đến "else" của if hoặc bên ngoài vòng lặp.

(*) Quy tắc thực sự là " nhánh tiến không được lấy, nhánh lùi được lấy ". Trong câu lệnh if, chỉ có một bước nhảy [chuyển tiếp] (đến điểm sau thân if) nếu điều kiện cho giá trị false (hãy nhớ rằng: CPU luôn giả định không thực hiện một nhánh / bước nhảy), nhưng trong một vòng lặp , có thể có một nhánh chuyển tiếp đến vị trí sau vòng lặp (không được lấy) và một nhánh lùi sau khi thay thế (được lấy).

Đây cũng là một trong những lý do tại sao lệnh gọi hàm ảo hoặc lệnh gọi hàm con trỏ không tệ hơn như nhiều người giả định ( http://phresnel.org/blog/ )


5

Như nhiều người đã chỉ ra, các nhánh có điều kiện có thể rất chậm trên một máy tính hiện đại.

Nói như vậy, có rất nhiều nhánh điều kiện không tồn tại trong câu lệnh if, bạn không thể luôn biết trình biên dịch sẽ đưa ra kết quả gì và lo lắng về việc các câu lệnh cơ bản sẽ mất bao lâu hầu như luôn là điều sai lầm. làm. (Nếu bạn có thể biết trình biên dịch sẽ tạo ra những gì một cách đáng tin cậy, bạn có thể không có trình biên dịch tối ưu hóa tốt.)


4

Điều duy nhất tôi có thể tưởng tượng điều này có thể đề cập đến thực tế là một ifcâu lệnh nói chung có thể dẫn đến một nhánh. Tùy thuộc vào các chi tiết cụ thể của kiến ​​trúc bộ xử lý, các nhánh có thể gây ra tình trạng ngừng đường ống hoặc các tình huống khác ít hơn tối ưu.

Tuy nhiên, đây là tình huống cực kỳ cụ thể - hầu hết các bộ vi xử lý hiện đại đều có khả năng dự đoán rẽ nhánh nhằm giảm thiểu tác động tiêu cực của việc phân nhánh. Một ví dụ khác là cách kiến ​​trúc ARM (và có thể là những kiến ​​trúc khác) có thể xử lý logic có điều kiện - ARM có thực thi điều kiện ở mức chỉ lệnh, do đó, logic có điều kiện đơn giản dẫn đến không phân nhánh - các lệnh chỉ thực thi dưới dạng NOP nếu các điều kiện không được đáp ứng.

Tất cả những gì đã nói - hãy hiểu logic của bạn chính xác trước khi lo lắng về những thứ này. Mã không chính xác không được tối ưu hóa như bạn có thể nhận được.


Tôi đã nghe nói rằng các hướng dẫn có điều kiện của ARM ngăn chặn ILP nên chúng có thể đang đẩy vấn đề xung quanh.
JD

3

CPU được kết nối sâu. Bất kỳ lệnh rẽ nhánh nào (if / for / while / switch / etc) có nghĩa là CPU không thực sự biết lệnh nào sẽ tải và chạy tiếp theo.

CPU hoặc ngừng hoạt động trong khi chờ đợi để biết phải làm gì, hoặc CPU đoán. Trong trường hợp của một CPU cũ hơn, hoặc nếu đoán sai, bạn sẽ phải gặp phải tình trạng ngưng trệ đường ống trong khi nó chạy và tải đúng lệnh. Tùy thuộc vào CPU, điều này có thể cao tới 10-20 lệnh có giá trị ngừng trệ.

Các CPU hiện đại cố gắng tránh điều này bằng cách thực hiện dự đoán rẽ nhánh tốt và bằng cách thực hiện nhiều đường dẫn cùng một lúc và chỉ giữ lại đường dẫn thực tế. Điều này giúp ích rất nhiều, nhưng chỉ có thể đi xa.

Chúc các bạn thành công trong lớp.

Ngoài ra, nếu bạn phải lo lắng về điều này trong cuộc sống thực, có thể bạn đang làm thiết kế hệ điều hành, đồ họa thời gian thực, máy tính khoa học hoặc thứ gì đó tương tự có sự ràng buộc về CPU. Hồ sơ trước khi lo lắng.


2

Viết chương trình của bạn theo cách rõ ràng nhất, đơn giản nhất, rõ ràng nhất mà rõ ràng là không hiệu quả. Điều đó làm cho việc sử dụng tốt nhất tài nguyên đắt tiền nhất, bạn. Có thể là viết hoặc sau đó gỡ lỗi (yêu cầu hiểu) chương trình. Nếu hiệu suất không đủ, hãy đo lườngcác nút thắt cổ chai ở đâu và xem cách giảm thiểu chúng. Chỉ trong những trường hợp cực kỳ hiếm hoi, bạn mới phải lo lắng về các hướng dẫn riêng lẻ (nguồn) khi làm như vậy. Hiệu suất là việc lựa chọn các thuật toán và cấu trúc dữ liệu phù hợp trong dòng đầu tiên, lập trình cẩn thận, có được một máy đủ nhanh. Sử dụng một trình biên dịch tốt, bạn sẽ ngạc nhiên khi thấy kiểu tái cấu trúc mã mà một trình biên dịch hiện đại thực hiện. Tái cấu trúc mã cho hiệu suất là một loại biện pháp cuối cùng, mã trở nên phức tạp hơn (do đó khó sửa đổi hơn), khó sửa đổi hơn và do đó đắt hơn.



0

Tôi đã có cuộc tranh luận này với một người bạn của tôi một lần. Anh ta đang sử dụng một thuật toán vòng tròn rất ngây thơ, nhưng tuyên bố anh ta nhanh hơn của tôi (Loại chỉ tính 1/8 của vòng tròn) vì tôi đã sử dụng if. Cuối cùng, câu lệnh if đã được thay thế bằng sqrt và bằng cách nào đó điều đó nhanh hơn. Có lẽ vì FPU đã tích hợp sqrt?


-1

Đắt nhất về cách sử dụng ALU? Nó sử dụng hết các thanh ghi CPU để lưu trữ các giá trị cần so sánh và mất thời gian để tìm nạp và so sánh các giá trị mỗi khi chạy câu lệnh if.

Do đó, tối ưu hóa điều đó là thực hiện một phép so sánh và lưu trữ kết quả dưới dạng một biến trước khi chạy vòng lặp.

Chỉ cố gắng giải thích những từ còn thiếu của bạn.

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.