Jika vs. Beralih Kecepatan


112

Pernyataan switch biasanya lebih cepat daripada pernyataan if-else-if yang setara (seperti yang dijelaskan di artikel ini ) karena pengoptimalan compiler.

Bagaimana sebenarnya pengoptimalan ini bekerja? Apakah ada yang punya penjelasan yang bagus?



Jawaban yang mungkin bagus: dotnetperls.com/if-switch-performance
Babak

Jawaban:


185

Kompiler dapat membuat tabel lompat jika memungkinkan. Misalnya, ketika Anda menggunakan reflektor untuk melihat kode yang dihasilkan, Anda akan melihat bahwa untuk sakelar besar pada string, kompilator akan benar-benar menghasilkan kode yang menggunakan tabel hash untuk mengirimkannya. Tabel hash menggunakan string sebagai kunci dan mendelegasikan casekode sebagai nilai.

Ini memiliki waktu proses asimtotik yang lebih baik daripada banyak ifpengujian berantai dan sebenarnya lebih cepat bahkan untuk string yang relatif sedikit.


6
Jawaban bagus, menarik tentang tabel hash.
BobbyShaftoe

4
Mereka juga mengubah ke perbandingan pohon dalam beberapa kasus. Alasannya agak kompleks tetapi pada dasarnya bermuara pada tabel tipuan yang mensterilkan buffer target lompatan cpu modern dan menghapus prediktor cabang. Saya samar-samar mengingat makalah di konferensi GCC tentang codegen untuk sakelar.
olliej

Artinya: switch (a) case "x": case "y": case "z": // sesuatu rusak; } lebih cepat dari: if (a == "x" || a == "b" || a == "c") // sesuatu yang benar?
yazanpro

di sini kita tidak bersarang jika lagi, hanya ATAU jadi bagaimana menurut Anda?
yazanpro

@yazanpro Pada kompiler lama berpotensi ya (tetapi perhatikan bahwa jumlah kasus sangat kecil sehingga mungkin tidak membuat perbedaan!). Kompiler modern melakukan lebih banyak analisis kode. Akibatnya, mereka mungkin mengetahui bahwa kedua cuplikan kode ini setara, dan menerapkan pengoptimalan yang sama. Tapi ini murni spekulasi di pihak saya, saya tidak tahu apakah ada kompiler yang benar-benar melakukan itu.
Konrad Rudolph

15

Ini adalah sedikit penyederhanaan seperti biasanya setiap kompiler modern yang menemukan if..else if ..urutan yang dapat dengan mudah diubah menjadi pernyataan switch oleh seseorang, kompiler juga akan melakukannya. Tetapi hanya untuk menambah kesenangan tambahan, kompilator tidak dibatasi oleh sintaks sehingga dapat menghasilkan pernyataan seperti "switch" secara internal yang memiliki campuran rentang, target tunggal, dll - dan mereka dapat (dan melakukan) melakukan ini untuk kedua switch dan if. pernyataan .else.

Anyhoo, perluasan dari jawaban Konrad adalah bahwa kompilator dapat menghasilkan tabel lompat, tetapi itu belum tentu dijamin (atau diinginkan). Untuk berbagai alasan, tabel lompat melakukan hal buruk pada prediktor cabang pada prosesor modern, dan tabel itu sendiri melakukan hal buruk pada perilaku cache, misalnya.

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

Jika kompilator benar-benar membuat tabel lompat untuk ini, kemungkinan akan lebih lambat daripada if..else if..kode gaya alternatif karena tabel lompat mengalahkan prediksi cabang.


4

Statistik tidak ada pertandingan mungkin tidak bagus.

Jika Anda benar-benar mengunduh sumber, nilai tidak ada kecocokan diketahui 21, baik dalam kasus if dan switch. Kompilator harus dapat mengabstraksi, mengetahui pernyataan mana yang harus dijalankan setiap saat, dan CPU harus dapat memprediksi cabang dengan benar.

Kasus yang lebih menarik adalah ketika tidak setiap kasus rusak, menurut pendapat saya, tetapi itu mungkin bukan cakupan eksperimen.


4

Pernyataan sakelar / kasus mungkin biasanya lebih cepat dalam 1 tingkat, tetapi ketika Anda mulai masuk ke 2 atau lebih, pernyataan sakelar / kasus mulai memakan waktu 2-3 kali lebih lama dari pernyataan if / else bersarang.

Artikel ini memiliki beberapa perbandingan kecepatan yang menyoroti perbedaan kecepatan saat pernyataan semacam itu bersarang.

Misalnya, menurut pengujian mereka, contoh kode seperti berikut:

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;

selesai setengah dari waktu yang diperlukan untuk menjalankan pernyataan switch / case:

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

Ya, itu contoh yang belum sempurna, tetapi itu menggambarkan maksudnya.

Jadi kesimpulannya mungkin menggunakan switch / case untuk tipe sederhana yang hanya sedalam satu level, tetapi untuk perbandingan yang lebih kompleks dan beberapa level bersarang, gunakan konstruksi if / else klasik?


-1: 1. Artikel ini sepenuhnya mengabaikan Prediksi Cabang, 2. algoritme tidak persis sama (jika ada yang lain di tautan sudah diberi kode lebih dioptimalkan) dan 3. perbedaan yang ditemukan sangat kecil sehingga tidak ada alasan penggunaan kode yang tepat dan bersih (sekitar 4 ns dalam 10.000.000 panggilan antara switch dan konstruksi if-else yang sama)
Trojaner

Contoh itu tidak akan dioptimalkan karena betapa sedikit kasus yang dimiliki blok sakelar. Biasanya setelah 5-6 elemen itu akan menghasilkan tabel lompat.
antiduh

0

Satu-satunya keuntungan dari kasus if over adalah ketika ada peningkatan frekuensi kejadian yang nyata dari kasus pertama.

Tidak yakin persis di mana ambangnya, tetapi saya menggunakan sintaks kasus kecuali yang pertama "hampir selalu" lulus tes pertama.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.