Ở 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 if
câu lệnh và các thử nghiệm điều khiển của for
và while
vò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 break
và continue
câu lệnh, goto
câ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 if
câ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 switch
câ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()
và max()
không phân nhánh.