Nếu so với Tốc độ chuyển đổi


112

Các câu lệnh switch thường nhanh hơn các câu lệnh if-else-if tương đương (ví dụ như được mô tả trong bài viết này ) do tối ưu hóa trình biên dịch.

Làm thế nào để tối ưu hóa này thực sự hoạt động? Có ai có một lời giải thích tốt?



Một câu trả lời hay có thể có: dotnetperls.com/if-switch-performance
Babak

Câu trả lời:


185

Trình biên dịch có thể xây dựng bảng nhảy nếu có. Ví dụ: khi bạn sử dụng phản xạ để xem mã được tạo, bạn sẽ thấy rằng đối với các công tắc lớn trên chuỗi, trình biên dịch sẽ thực sự tạo ra mã sử dụng bảng băm để gửi chúng. Bảng băm sử dụng các chuỗi làm khóa và ủy quyền cho các casemã dưới dạng giá trị.

Điều này có thời gian chạy tiệm cận tốt hơn so với nhiều ifbài kiểm tra theo chuỗi và thực sự nhanh hơn ngay cả đối với tương đối ít chuỗi.


6
Câu trả lời hay, thú vị về bảng băm.
BobbyShaftoe

4
Chúng cũng chuyển đổi sang so sánh cây trong một số trường hợp. Lý do hơi phức tạp nhưng về cơ bản tóm tắt là bảng hướng dẫn sử dụng bộ đệm mục tiêu nhảy cpu hiện đại và do đó xóa sạch dự đoán nhánh. Tôi mơ hồ nhớ lại một bài báo tại một hội nghị GCC về codegen cho thiết bị chuyển mạch.
olliej

Điều đó có nghĩa là: switch (a) case "x": case "y": case "z": // cái gì đó phá vỡ; } nhanh hơn: if (a == "x" || a == "b" || a == "c") // cái gì đó phải không?
yazanpro

ở đây chúng ta không có lồng nếu khác, chỉ HOẶC vậy bạn nghĩ sao?
yazanpro

@yazanpro Trên các trình biên dịch cũ có khả năng có (nhưng lưu ý rằng số lượng trường hợp quá nhỏ nên nó có thể không tạo ra sự khác biệt!). Mặc dù vậy, các trình biên dịch hiện đại thực hiện nhiều phân tích mã hơn. Do đó, họ có thể nhận ra rằng hai đoạn mã này là tương đương và áp dụng các cách tối ưu hóa giống nhau. Nhưng đây là suy đoán thuần túy về phía tôi, tôi không biết liệu có trình biên dịch nào thực sự làm điều đó hay không.
Konrad Rudolph

15

Đây là một sự đơn giản hóa nhỏ vì thông thường bất kỳ trình biên dịch hiện đại nào gặp phải một if..else if ..trình tự có thể được một người chuyển đổi thành câu lệnh switch một cách đáng kể, trình biên dịch cũng sẽ như vậy. Nhưng chỉ để thêm phần thú vị, trình biên dịch không bị hạn chế bởi cú pháp vì vậy có thể tạo "switch" như các câu lệnh bên trong có sự kết hợp của phạm vi, mục tiêu đơn lẻ, v.v. - và chúng có thể (và làm) làm điều này cho cả switch và if. .else câu lệnh.

Anyhoo, một phần mở rộng cho câu trả lời của Konrad là trình biên dịch có thể tạo bảng nhảy, nhưng điều đó không nhất thiết phải đảm bảo (cũng không mong muốn). Vì nhiều lý do khác nhau, các bảng nhảy có tác dụng xấu đối với các yếu tố dự đoán nhánh trên các bộ xử lý hiện đại và bản thân các bảng này thực hiện các tác vụ xấu đối với hành vi của bộ đệm, ví dụ:

switch(a) { case 0: ...; break; case 1: ...; break; }

Nếu một trình biên dịch thực sự tạo một bảng nhảy cho điều này, thì if..else if..mã kiểu thay thế sẽ chậm hơn vì bảng nhảy đánh bại dự đoán nhánh.


4

Số liệu thống kê không có trận đấu có thể không tốt.

Nếu bạn thực sự tải xuống nguồn, không có giá trị khớp nào được biết là 21, trong cả trường hợp if và switch. Một trình biên dịch phải có thể trừu tượng hóa, biết câu lệnh nào nên được chạy mọi lúc và CPU phải có thể phân nhánh dự đoán đúng cách.

Theo tôi, trường hợp thú vị hơn là khi không phải trường hợp nào cũng vỡ, nhưng đó có thể không phải là phạm vi của thử nghiệm.


4

Các câu lệnh switch / case thường có thể nhanh hơn sâu 1 cấp, nhưng khi bạn bắt đầu vào 2 hoặc nhiều hơn, các câu lệnh switch / case sẽ bắt đầu mất thời gian gấp 2-3 lần các câu lệnh if / else lồng nhau.

Bài viết này có một số so sánh tốc độ nêu bật sự khác biệt về tốc độ khi các câu lệnh như vậy được lồng vào nhau.

Ví dụ: theo thử nghiệm của họ, mã mẫu như sau:

if (x % 3 == 0)
            if (y % 3 == 0)
                total += 3;
            else if (y % 3 == 1)
                total += 2;
            else if (y % 3 == 2)
                total += 1;
            else
                total += 0;
        else if (x % 3 == 1)
            if (y % 3 == 0)
                total += 3;
            else if (y % 3 == 1)
                total += 2;
            else if (y % 3 == 2)
                total += 1;
            else
                total += 0;
        else if (x % 3 == 2)
            if (y % 3 == 0)
                total += 3;
            else if (y % 3 == 1)
                total += 2;
            else if (y % 3 == 2)
                total += 1;
            else
                total += 0;
        else
            if (y % 3 == 0)
                total += 3;
            else if (y % 3 == 1)
                total += 2;
            else if (y % 3 == 2)
                total += 1;
            else
                total += 0;

đã hoàn thành trong một nửa thời gian mà câu lệnh switch / case tương đương đã chạy:

switch (x % 3)
    {
        case 0:
            switch (y % 3)
            {
                case 0: total += 3;
                    break;
                case 1: total += 2;
                    break;
                case 2: total += 1;
                    break;
                default: total += 0;
                    break;
            }
            break;
        case 1:
            switch (y % 3)
            {
                case 0: total += 3;
                    break;
                case 1: total += 2;
                    break;
                case 2: total += 1;
                    break;
                default: total += 0;
                    break;
            }
            break;
    case 2:
            switch (y % 3)
            {
                case 0: total += 3;
                    break;
                case 1: total += 2;
                    break;
                case 2: total += 1;
                    break;
                default: total += 0;
                    break;
            }
            break;
    default:
        switch (y % 3)
        {
            case 0: total += 3;
                break;
            case 1: total += 2;
                break;
            case 2: total += 1;
                break;
            default: total += 0;
                break;
        }
        break;
    }

Vâng, đó là một ví dụ thô sơ, nhưng nó minh họa quan điểm.

Vì vậy, một kết luận có thể là sử dụng switch / case cho các kiểu đơn giản chỉ sâu một cấp, nhưng đối với các so sánh phức tạp hơn và nhiều cấp lồng nhau, hãy sử dụng cấu trúc if / else cổ điển?


-1: 1. Bài báo hoàn toàn bỏ qua Dự đoán Nhánh, 2. các thuật toán không hoàn toàn giống nhau (nếu-else duy nhất trên liên kết đã được mã hóa tối ưu hơn) và 3. sự khác biệt được tìm thấy rất nhỏ nên không có gì lý giải việc sử dụng mã sạch, thích hợp (khoảng 4 ns trong 10.000.000 cuộc gọi giữa công tắc và cấu trúc if-else tương tự)
Trojaner

Ví dụ đó sẽ không được tối ưu hóa vì khối công tắc có rất ít trường hợp. Thông thường, sau 5-6 phần tử, nó sẽ tạo ra một bảng nhảy.
antiduh

0

Ưu điểm duy nhất của if over case là khi tần suất xuất hiện của case đầu tiên tăng lên đáng kể.

Không chắc chắn chính xác ngưỡng nằm ở đâu, nhưng tôi sử dụng cú pháp trường hợp trừ khi "hầu như luôn luôn" đầu tiên vượt qua thử nghiệm đầu tiê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.