İf-else deyimini değiştirmenin avantajı


168

Yaklaşık 10 tanesinin beklenen bir eylemi olduğu (şu anda aynı eylemdir) 30 numaralandırma için switchbir ififade kullanmak yerine ifade kullanmak için en iyi uygulama nedir unsigned? Performans ve alan dikkate alınmalıdır, ancak kritik değildir. Parçacığı soyutladım, bu yüzden adlandırma kuralları için benden nefret etmeyin.

switch Beyan:

// numError is an error enumeration type, with 0 being the non-error case
// fire_special_event() is a stub method for the shared processing

switch (numError)
{  
  case ERROR_01 :  // intentional fall-through
  case ERROR_07 :  // intentional fall-through
  case ERROR_0A :  // intentional fall-through
  case ERROR_10 :  // intentional fall-through
  case ERROR_15 :  // intentional fall-through
  case ERROR_16 :  // intentional fall-through
  case ERROR_20 :
  {
     fire_special_event();
  }
  break;

  default:
  {
    // error codes that require no additional action
  }
  break;       
}

if Beyan:

if ((ERROR_01 == numError)  ||
    (ERROR_07 == numError)  ||
    (ERROR_0A == numError)  || 
    (ERROR_10 == numError)  ||
    (ERROR_15 == numError)  ||
    (ERROR_16 == numError)  ||
    (ERROR_20 == numError))
{
  fire_special_event();
}

26
Bu 'öznel' olarak düzenlenmiş mi? Gerçekten mi? Elbette 'öznel' şu ya da bu şekilde kanıtlanamayan şeyler içindir?
Alexandra Franks

Elbette en verimli kodu üreten noktadan görebilirsiniz, ancak herhangi bir modern derleyici eşit derecede verimli olmalıdır. Sonunda, bu daha çok bisiklet kulübesinin rengiyle ilgili bir soru.
jfs

8
Kabul etmiyorum, bunun öznel olduğunu düşünmüyorum. Basit bir ASM farkı önemlidir, çoğu durumda birkaç saniyelik optimizasyonu göz ardı edemezsiniz. Ve bu soruda, dini bir savaş ya da tartışma değil, birinin neden daha hızlı olacağının rasyonel bir açıklaması var, sadece kabul edilen cevabı okuyun.
chakrit


@RichardFranks offtopic: rendeler! Şimdiye kadar gördüğüm SO üzerinde
denetlenen

Yanıtlar:


162

Anahtarı kullanın.

En kötü durumda derleyici if-else zinciri ile aynı kodu üretir, böylece hiçbir şey kaybetmezsiniz. Şüpheniz varsa, en yaygın vakaları önce switch deyimine koyun.

En iyi durumda, optimize edici kodu oluşturmak için daha iyi bir yol bulabilir. Bir derleyicinin yaptığı ortak şey, ikili bir karar ağacı oluşturmak (karşılaştırmaları ve ortalama durumda atlar) veya sadece bir atlama tablosu oluşturmaktır (hiç karşılaştırılmadan çalışır).


2
Teknik olarak, enum değerinin atlama tablosunda olduğundan emin olmak için hala bir karşılaştırma olacaktır.
0124816

Jep. Bu doğru. Numaralandırmaların açılması ve tüm vakaların ele alınması son karşılaştırmadan kurtulabilir.
Nils Pipenbrinck

4
Bir dizi ifs'ın teorik olarak bir derleyici tarafından yapılan bir anahtarla aynı şekilde analiz edilebileceğini unutmayın, ama neden şansınızı değerlendirelim? Bir anahtar kullanarak, tam olarak istediğiniz şeyi iletiyorsunuz, bu da kod üretimini kolaylaştırıyor.
jakobengblom2

5
jakoben: Bu yapılabilir, ancak sadece / benzeri zincirler için anahtar benzeri. Uygulamada bunlar programcılar anahtar kullandığından gerçekleşmez. Derleyici teknolojisine girdim ve bana güvendim: Böyle "işe yaramaz" yapılar bulmak çok zaman alıyor. Derleyici çocuklar için böyle bir optimizasyon mot anlamlıdır.
Nils Pipenbrinck

5
Sözde özyinelemeli bina kolaylıkla @NilsPipenbrinck if- elseşablon meta programlamada zincirleri ve üretme zorluğu switch casezincirlerini, o haritalama daha önemli hale gelebilir. (ve evet, eski yorum, ancak web sonsuza kadar veya en azından bir sonraki salı
gününe

45

Örneğinizde sağladığınız özel durum için en açık kod muhtemelen:

if (RequiresSpecialEvent(numError))
    fire_special_event();

Açıkçası bu sadece sorunu kodun farklı bir alanına taşır, ancak şimdi bu testi tekrar kullanma şansına sahipsiniz. Ayrıca nasıl çözüleceğine dair daha fazla seçeneğiniz var. Std :: set kullanabilirsiniz, örneğin:

bool RequiresSpecialEvent(int numError)
{
    return specialSet.find(numError) != specialSet.end();
}

Bu, bir seçenek olduğunu, bu RequespecialEvent en iyi uygulaması olduğunu önermiyorum. Yine de, bir anahtar veya if-else zinciri ya da bir arama tablosu ya da değer üzerinde herhangi bir bit manipülasyonu kullanabilirsiniz. Karar verme süreciniz ne kadar belirsiz olursa, yalıtılmış bir işleve sahip olmanızdan o kadar çok değer elde edersiniz.


5
Bu çok doğru. Okunabilirlik, hem anahtardan hem de if ifadelerinden çok daha iyidir. Aslında böyle bir şeye kendim cevap verecektim, ama sen beni dövüyordun. :-)
mlarsen

Enum değerlerinizin tümü küçükse, bir karmaya ihtiyacınız yoktur, sadece bir tablo. Örneğin const std::bitset<MAXERR> specialerror(initializer); ile birlikte kullanın if (specialerror[numError]) { fire_special_event(); }. Sınır denetimi istiyorsanız, sınır bitset::test(size_t)dışındaki değerlere bir istisna atar. ( bitset::operator[]aralık kontrolü yapmaz). cplusplus.com/reference/bitset/bitset/test . Bu muhtemelen derleyici tarafından üretilen bir atlama tablosu uygulaması switch, esp. bunun tek bir alınmayan dal olacağı özel olmayan durumda.
Peter Cordes

@PeterCordes Hala masayı kendi işlevine sokmanın daha iyi olduğunu savunuyorum. Söylediğim gibi , bunu yaptığınızda açılan birçok seçenek var, hepsini saymaya çalışmadım.
Mark Ransom

@ MarkRansom: Soyutlamaya katılmıyorum demek istemedim. Kullanarak örnek bir uygulama yaptığınızdan std::set, bunun muhtemelen kötü bir seçim olduğuna dikkat çekeceğimi düşündüm. Gcc'nin bir bitmap'i 32 bit hemen test etmek için OP kodunu zaten derlediği ortaya çıktı. godbolt: goo.gl/qjjv0e . gcc 5.2 bunu ifsürüm için bile yapacak . Ayrıca, daha yeni gcc, biraz doğru yere btkoymak 1ve kullanmak için değiştirmek yerine bit testi talimatını kullanacaktır test reg, imm32.
Peter Cordes

Bu hemen sabit bitmap büyük bir kazançtır, çünkü bitmap'te önbellek kaçışı yoktur. "Özel" hata kodlarının tümü 64 veya daha düşük bir aralıktaysa çalışır. (veya eski 32bit kod için 32.) Derleyici, sıfırdan farklıysa, en küçük harf değerini çıkarır. Buradaki paket, son derleyicilerin, hantal bir veri yapısı kullanmasını söylemediğiniz sürece, kullandığınız mantıktan iyi kod alabileceğiniz kadar akıllı olmasıdır.
Peter Cordes

24

Anahtar daha hızlıdır.

Bir döngü içinde 30 farklı değer olup olmadığını deneyin ve anahtarın ne kadar hızlı olduğunu görmek için anahtarı kullanarak aynı kodla karşılaştırın.

Şimdi, anahtarın gerçek bir sorunu var : Anahtar derleme zamanında her durumdaki değerleri bilmelidir. Bu şu kod anlamına gelir:

// WON'T COMPILE
extern const int MY_VALUE ;

void doSomething(const int p_iValue)
{
    switch(p_iValue)
    {
       case MY_VALUE : /* do something */ ; break ;
       default : /* do something else */ ; break ;
    }
}

derlenmeyecek.

Çoğu kişi tanımları (Aargh!) Kullanır ve diğerleri aynı derleme birimindeki sabit değişkenleri bildirir ve tanımlar. Örneğin:

// WILL COMPILE
const int MY_VALUE = 25 ;

void doSomething(const int p_iValue)
{
    switch(p_iValue)
    {
       case MY_VALUE : /* do something */ ; break ;
       default : /* do something else */ ; break ;
    }
}

Sonuç olarak, geliştirici "hız + netlik" ile "kod bağlantısı" arasında seçim yapmalıdır.

(Bir anahtarın cehennem gibi kafa karıştırıcı olması için yazılamaz ... Şu anda gördüğüm çoğu anahtar bu "kafa karıştırıcı" kategoriden "... Ama bu başka bir hikaye ...)

2008-09-21'i düzenleyin:

bk1e şu yorumu ekledi: " Sabitleri bir başlık dosyasında numaralandırmalar olarak tanımlamak bunu işlemenin başka bir yoludur".

Tabiki öyle.

Harici tipin noktası değeri kaynaktan ayırmaktı. Bu değeri bir makro, basit bir int int bildirimi olarak veya hatta bir numaralandırma olarak tanımlamak, değeri satır içine almanın yan etkisine sahiptir. Bu nedenle, tanım, enum değeri veya sabit int değer değişirse, yeniden derleme gerekli olacaktır. Harici beyan, değer değişikliği durumunda yeniden derlemeye gerek olmadığı anlamına gelir, ancak diğer yandan anahtarın kullanılmasını imkansız hale getirir. Anahtar kullanma sonucu , anahtar kodu ile vaka olarak kullanılan değişkenler arasındaki bağlantıyı artıracaktır . Tamam olduğunda, anahtarı kullanın. Değilse, o zaman sürpriz olmaz.

.

2013-01-15'i düzenleyin:

Vlad Lazarenko cevabımı yorumladı ve bir anahtarın oluşturduğu montaj kodu üzerine derinlemesine çalışmasına bir bağlantı verdi. Çok aydınlatıcı: http://lazarenko.me/switch/


Üstbilgi dosyasında sabitleri numaralandırma olarak tanımlamak bunu işlemenin başka bir yoludur.
bk1e


1
@Vlad Lazarenko: Bağlantı için teşekkürler! Çok ilginç bir okumaydı.
paercebal

1
@AhmedHussein user404725'in bağlantısı öldü. Neyse ki, WayBack Machine'de buldum: web.archive.org/web/20131111091431/http://lazarenko.me/2013/01/… . Gerçekten, WayBack Makinesi oldukça bir nimet olabilir.
Jack Giffin

Çok teşekkürler, çok faydalı
Ahmed Hussein

20

Derleyici zaten optimize edecek - en okunabilir olduğu için anahtara gidin.


3
Büyük olasılıkla derleyici if-then-else dokunmaz. Aslında, gccbunu kesin olarak yapmayacak (bunun için iyi bir neden var). Clang her iki durumu da bir ikili aramaya dönüştürecektir. Örneğin, bakınız bu .

7

Anahtar yalnızca okunabilirlik içinse. Bence dev ifadelerin bakımı daha zor ve okunması daha zor.

ERROR_01 : // kasıtlı düşme

veya

(ERROR_01 == numError) ||

İkincisi daha fazla hataya eğilimlidir ve ilkinden daha fazla yazma ve biçimlendirme gerektirir.


6

Okunabilirlik kodu. Neyin daha iyi performans gösterdiğini bilmek istiyorsanız, optimizasyonlar ve derleyiciler değiştikçe bir profil oluşturucu kullanın ve performans sorunları nadiren insanların düşündükleri yerdir.


6

Anahtarı kullanın, bunun için ve programcıların beklediği şey budur.

Gereksiz vaka etiketlerini yerleştirirdim - sadece insanları rahat hissettirmek için, kuralların ne zaman / ne olduğunu dışarıda bırakmak için ne olduğunu hatırlamaya çalışıyordum.
Üzerinde çalışan bir sonraki programcının dil ayrıntıları hakkında gereksiz düşünmeler yapmasını istemezsiniz (birkaç ay içinde siz olabilirsiniz!)


4

Derleyiciler optimizasyonda gerçekten iyidir switch. Son gcc aynı zamanda bir grup koşulu optimize etmekte de iyidir.if .

Godbolt üzerinde bazı test senaryoları yaptım .

Ne zaman case değerleri birbirine yakın gruplanmış, gcc, çınlama ve icc bir değer özel olanlardan biri olup olmadığını kontrol etmek için bir bit eşlem kullanmak için tüm akıllı yeterlidir.

örneğin gcc 5.2-O3, ' switchyi (ve ifçok benzer bir şeyi) derler :

errhandler_switch(errtype):  # gcc 5.2 -O3
    cmpl    $32, %edi
    ja  .L5
    movabsq $4301325442, %rax   # highest set bit is bit 32 (the 33rd bit)
    btq %rdi, %rax
    jc  .L10
.L5:
    rep ret
.L10:
    jmp fire_special_event()

Bitmap'in hemen veri olduğuna dikkat edin, bu nedenle ona erişen herhangi bir veri önbelleği kaçırması veya bir atlama tablosu yoktur.

gcc 4.9.2-O3 switch, bir bitmap'i derler , ancak 1U<<errNumbermov / shift ile yapar . ifSürümü şube serisine derler .

errhandler_switch(errtype):  # gcc 4.9.2 -O3
    leal    -1(%rdi), %ecx
    cmpl    $31, %ecx    # cmpl $32, %edi  wouldn't have to wait an extra cycle for lea's output.
              # However, register read ports are limited on pre-SnB Intel
    ja  .L5
    movl    $1, %eax
    salq    %cl, %rax   # with -march=haswell, it will use BMI's shlx to avoid moving the shift count into ecx
    testl   $2150662721, %eax
    jne .L10
.L5:
    rep ret
.L10:
    jmp fire_special_event()

1'den errNumber( çıkarma leaişlemini bir hareketle birleştirmek için) nasıl çıkardığına dikkat edin. Bu, bitmap'i 32bit acil duruma sığdırır ve 64bit acil durumdan kaçınırmovabsq daha fazla talimat baytı alan .

Daha kısa (makine kodunda) bir dizi şöyle olur:

    cmpl    $32, %edi
    ja  .L5
    mov     $2150662721, %eax
    dec     %edi   # movabsq and btq is fewer instructions / fewer Intel uops, but this saves several bytes
    bt     %edi, %eax
    jc  fire_special_event
.L5:
    ret

(Kullanılamaması jc fire_special_eventher yerde mevcuttur ve bir derleyici hatasıdır .)

rep retAMD K8 ve K10'un (Buldozer öncesi) yararına şube hedeflerinde ve aşağıdaki koşullu dallarda kullanılır: `` ret ret` ne demektir? . Bu olmadan, şube tahmini bu eski CPU'larda da işe yaramaz.

bt(bit testi) register arg ile hızlıdır. Bir 1'i errNumberbitlerle sola kaydırma ve a yapma işini birleştirir test, ancak yine de 1 döngü gecikmesi ve sadece tek bir Intel uop'dur. CISC yolu semantiği nedeniyle bir bellek argümanıyla yavaştır: "bit dizesi" için bir bellek işleneni ile, test edilecek baytın adresi diğer argümana (8'e bölünür) dayalı olarak hesaplanır ve bellek işleneninin işaret ettiği 1, 2, 4 veya 8 bayt yığınla sınırlı değildir.

Gönderen Agner Fog'un talimat tabloları , değişken sayımı kaydırma talimatı bir daha yavaş btson Intel (2 UOPs yerine 1 ve Vardiyam ihtiyacı olduğunu her şeyi yapmaz) üzerinde.


4

Başka bir durum söz konusu olduğunda switch () 'in avantajları şunlardır: - 1. Anahtar, başka bir ifadeye göre çok daha verimlidir çünkü her bir durum, bireysel ifadenin doğru veya yanlış durum için kontrol edilmesi gereken başka bir durumdan farklı olarak önceki duruma bağlı değildir.

  1. Hayır olduğunda. ifadesi tek bir ifade için değerlerden oluşuyorsa, switch case başka bir durumda daha esnektir çünkü aksi durumda yargılama yalnızca iki değere (yani true veya false) dayanır.

  2. Anahtardaki değerler kullanıcı tanımlıyken, başka bir durumda ise değerler kısıtlamalara dayanır.

  3. Hata durumunda, anahtardaki ifadeler kolayca çapraz kontrol edilebilir ve düzeltilebilir; bu, eğer başka bir ifadenin durumunda kontrol edilmesi nispeten güçtür.

  4. Anahtar kutusu çok daha kompakt ve okunması ve anlaşılması kolaydır.


2

IMO, bu geçişin ne için yapıldığının mükemmel bir örneğidir.


c # 'da düşme düşüncesinin gerçekleştiği tek durum budur. Orada iyi bir tartışma var.
BCS

2

Vakalarınızın gelecekte gruplanmış olması muhtemelse - birden fazla vaka bir sonuca karşılık geliyorsa - anahtarın okunması ve bakımı daha kolay olabilir.


2

Eşit derecede iyi çalışırlar. Modern bir derleyici göz önüne alındığında performans hemen hemen aynıdır.

Daha okunabilir ve daha esnek olduklarından, ifadelerin büyük / küçük harf ifadeleri yerine tercih edilmesini tercih ederim. "|| max <min" gibi sayısal eşitliğe dayanmayan başka koşullar ekleyebilirsiniz. Ancak burada yayınladığınız basit dava için, gerçekten önemli değil, sadece sizin için en okunabilir olanı yapın.


2

anahtar kesinlikle tercih edilir. Bir anahtarın vaka listesine bakmak ve ne yaptığını kesin olarak bilmek, uzun süre koşulunu okumaktan daha kolaydır.

Durumdaki tekrarlama ifgözler üzerinde zordur. Varsayalım ki bunlardan biri ==yazılmıştır !=; fark eder misin Ya da 'numError' örneğinin derlendiği 'nmuError' yazıldıysa hangisi derlendi?

Genellikle anahtar yerine polimorfizm kullanmayı tercih ederdim, ancak bağlamla ilgili daha fazla ayrıntı olmadan söylemek zor.

Performansa gelince, en iyi bahis, uygulamanızın performansını vahşi doğada beklediğinize benzer koşullarda ölçmek için bir profil oluşturucu kullanmaktır. Aksi takdirde, muhtemelen yanlış yerde ve yanlış şekilde optimizasyon yapıyorsunuzdur.


2

Anahtar çözümünün uygunluğuna katılıyorum, ancak IMO , anahtarı burada ele geçiriyorsunuz .
Anahtarın amacı , değere bağlı olarak farklı kullanımlara sahip olmaktır .
Algo'yu sözde kodda açıklamak zorunda olsaydın, bir if kullanacaksın, çünkü anlamsal olarak, işte budur: whatever_error bunu yaparsa ... Bir
gün kodunu her hata için belirli bir kod olacak şekilde değiştirmek istemiyorsan , Eğer kullanırdım .


2
Aynı nedenden dolayı düşme davasına katılmıyorum. Anahtarı "01,07,0A, 10,15,16 ve 20 yangın özel olaylarında" olarak okudum. Başka bir bölüme düşme yok., Bu sadece her değer için 'case' anahtar kelimesini tekrarladığınız C ++ sözdiziminin bir eseridir.
MSalters

1

Bazılarının aynı fikirde olmayacağından emin olsam da, açıklık ve kongre adına if ifadesini seçerdim. Sonuçta, ifbazı koşullar doğru bir şey yapmak istiyorsun ! Tek bir işlemle geçiş yapmak biraz ... gereksiz görünüyor.


1

SWITCH kullan diyebilirim. Bu şekilde sadece farklı sonuçlar uygulamanız gerekir. On özdeş vakanız varsayılanı kullanabilir. Bir değişiklik yapmanız gereken tek şey değişikliği açıkça uygulamaksa, varsayılanı düzenlemenize gerek yoktur. SWITCH'e vaka eklemek veya çıkarmak, IF ve ELSEIF'i düzenlemekten çok daha kolaydır.

switch(numerror){
    ERROR_20 : { fire_special_event(); } break;
    default : { null; } break;
}

Belki de durumunuzu (bu durumda numerror) bir olasılıklar listesine karşı test edin, belki de bir dizi, bu yüzden kesinlikle bir sonuç olmayacaksa ANAHTARINIZ bile kullanılmaz.


Toplamda yaklaşık 30 hata var. 10 özel eylem gerektirir, bu yüzden bir eylem gerektirmeyen ~ 20 hataları için varsayılan kullanıyorum ...
Zing-

1

Sadece 30 hata koduna sahip olduğunuzu görünce, kendi atlama tablonuzu kodlayın, sonra derleyicinin doğru şeyi yapmasını ummak yerine tüm optimizasyon seçimlerini kendiniz yapın (atlama her zaman en hızlı olacaktır). Ayrıca kodu çok küçük hale getirir (atlama tablosunun statik bildirimi dışında). Ayrıca, yalnızca tablo verilerini doğrudan atarak, bir hata ayıklayıcı ile çalışma zamanında davranışı değiştirebilmenizin yan faydası vardır.


Vay canına, bu basit bir problemi karmaşık bir soruna dönüştürmenin bir yolu gibi görünüyor. Derleyici sizin için iyi bir iş çıkardığında neden tüm bu sorunlara gidiyoruz. Ayrıca görünüşe göre bir hata giderici, bu yüzden hız açısından kritik olma olasılığı yüksek değil. Bir anahtar, okunması ve bakımı en kolay şeydir.
MrZebra

Bir tablo neredeyse karmaşık değildir - aslında kod geçişinden daha basittir. Açıklamada performansın bir faktör olduğu belirtildi.
Greg Whitfield

Erken optimizasyon gibi geliyor. Enum değerlerinizi küçük ve bitişik tuttuğunuz sürece, derleyici bunu sizin için yapmalıdır. Anahtarı ayrı bir işleve koymak, Mark Ransom'un cevabında önerdiği gibi, onu kullanan kodu güzel ve küçük tutar, aynı küçük kod avantajı sağlar.
Peter Cordes

Ayrıca, kendiniz bir şey uygulayacaksanız std::bitset<MAXERR> specialerror;, o zaman bir if (specialerror[err]) { special_handler(); }. Bu bir atlama tablosundan daha hızlı olacak, esp. alınmayan davada.
Peter Cordes

1

En iyi uygulamadan emin değilim, ancak anahtarı kullanırdım - ve sonra 'varsayılan' aracılığıyla kasıtlı düşmeyi yakalardım


1

Estetik olarak bu yaklaşımı destekleme eğilimindeyim.

unsigned int special_events[] = {
    ERROR_01,
    ERROR_07,
    ERROR_0A,
    ERROR_10,
    ERROR_15,
    ERROR_16,
    ERROR_20
 };
 int special_events_length = sizeof (special_events) / sizeof (unsigned int);

 void process_event(unsigned int numError) {
     for (int i = 0; i < special_events_length; i++) {
         if (numError == special_events[i]) {
             fire_special_event();
             break;
          }
     }
  }

Verileri biraz daha akıllı hale getirin, böylece mantığı biraz daha kalın hale getirelim.

Tuhaf göründüğünün farkındayım. İşte ilham (Python'da nasıl yapacağımdan):

special_events = [
    ERROR_01,
    ERROR_07,
    ERROR_0A,
    ERROR_10,
    ERROR_15,
    ERROR_16,
    ERROR_20,
    ]
def process_event(numError):
    if numError in special_events:
         fire_special_event()

4
Bir dilin sözdizimi gelmez bir çözüm uygulamak nasıl bir etkisi var ... => Python ile C çirkin görünüyor ve güzel. :)
rlerallut

Bitmap'ler mi kullanıyorsunuz? Error_0a 0x0a vb ise, onları uzun bir süre bit olarak koyabilirsiniz. uzun uzun special_events = 1LL << 1 | 1LL << 7 | 1LL << 0xa ... O zaman eğer kullanın (special_events & (1LL << numError) fire_special_event ()
paperhorse

1
Yuck. Bir O (1) en kötü durum işlemini (atlama tabloları oluşturulmuşsa) O (N) en kötü duruma (N, ele alınan vaka sayısıdır) dönüştürdünüz ve bir breakdış case(evet, küçük günah, ama yine de bir günah). :)
Mac

Yuck? Performans ve alanın kritik olmadığını söyledi. Soruna bakmak için başka bir yol öneriyordum. Bir sorunu insanların daha az düşünecekleri bir şekilde temsil edebilirsek, genellikle bilgisayarların daha fazla düşünmesi gerektiği anlamına gelmez.
mbac32768

1
while (true) != while (loop)

Muhtemelen birincisi derleyici tarafından optimize edilmiştir, bu da döngü sayısını arttırırken ikinci döngünün neden daha yavaş olduğunu açıklar.


Bu McAnix'in cevabına bir yorum gibi görünüyor. Bu sadece zamanlama o girişimi ile sorunlardan biri ifvs. switchJava bir döngü sonu koşulu olarak.
Peter Cordes

1

Programı derlemek söz konusu olduğunda, herhangi bir fark olup olmadığını bilmiyorum. Ancak programın kendisi ve kodu olabildiğince basit tutmak, kişisel olarak ne yapmak istediğinize bağlı olduğunu düşünüyorum. else if else ifadelerinin avantajları var, bence:

bir değişkeni belirli aralıklarla test etmenize olanak tanır ve koşullu olarak (Standart Kütüphane veya Kişisel) işlevleri kullanabilirsiniz.

(misal:

`int a;
 cout<<"enter value:\n";
 cin>>a;

 if( a > 0 && a < 5)
   {
     cout<<"a is between 0, 5\n";

   }else if(a > 5 && a < 10)

     cout<<"a is between 5,10\n";

   }else{

       "a is not an integer, or is not in range 0,10\n";

Ancak, eğer başka durumlarda acele karmaşık ve dağınık (en iyi girişimlerinize rağmen) alabilirsiniz. Anahtar ifadeleri daha açık, daha temiz ve daha kolay okunur olma eğilimindedir; ancak yalnızca belirli değerlere karşı test yapmak için kullanılabilir (örnek:

`int a;
 cout<<"enter value:\n";
 cin>>a;

 switch(a)
 {
    case 0:
    case 1:
    case 2: 
    case 3:
    case 4:
    case 5:
        cout<<"a is between 0,5 and equals: "<<a<<"\n";
        break;
    //other case statements
    default:
        cout<<"a is not between the range or is not a good value\n"
        break;

Eğer if - else if - else ifadelerini tercih ederim, ama gerçekten size kalmış. İşlevleri koşul olarak kullanmak istiyorsanız veya bir aralığı, dizi veya vektöre karşı bir şey test etmek istiyorsanız ve / veya karmaşık iç içe geçme ile uğraşmak istemiyorsanız, eğer başka bloklar varsa If kullanmanızı öneririz. Tek değerlere karşı test etmek veya temiz ve okunması kolay bir blok istiyorsanız, switch () büyük / küçük harf bloklarını kullanmanızı tavsiye ederim.


0

Ben hız ve bellek kullanımı hakkında size söyleyecek kişi değilim, ancak bir anahtar ifadesine bakmak çok daha kolay anlaşılması çok daha büyük bir if ifadesi (özellikle 2-3 ay aşağı)


0

Eski olduğunu biliyorum ama

public class SwitchTest {
static final int max = 100000;

public static void main(String[] args) {

int counter1 = 0;
long start1 = 0l;
long total1 = 0l;

int counter2 = 0;
long start2 = 0l;
long total2 = 0l;
boolean loop = true;

start1 = System.currentTimeMillis();
while (true) {
  if (counter1 == max) {
    break;
  } else {
    counter1++;
  }
}
total1 = System.currentTimeMillis() - start1;

start2 = System.currentTimeMillis();
while (loop) {
  switch (counter2) {
    case max:
      loop = false;
      break;
    default:
      counter2++;
  }
}
total2 = System.currentTimeMillis() - start2;

System.out.println("While if/else: " + total1 + "ms");
System.out.println("Switch: " + total2 + "ms");
System.out.println("Max Loops: " + max);

System.exit(0);
}
}

Döngü sayısının değiştirilmesi çok değişir:

Eğer ise / else: 5ms Anahtar: 1ms Max Döngüler: 100000

Eğer ise / else: 5ms Anahtar: 3ms Max Döngüler: 1000000

Eğer ise / else: 5ms Anahtar: 14ms Max Döngü: 10000000

Eğer / else ise: 5ms Anahtar: 149ms Maksimum Döngü: 100000000

(isterseniz daha fazla ifade ekleyin)


3
Güzel bir nokta, ama sry, ahbap, yanlış dildin. Dili değiştirmek çok değişir;)
Gabriel Schreiber

2
if(max) breakDöngü bakılmaksızın döngü sayımı sabit zamanda çalışır? JIT-derleyicisi, döngüyü optimize etmek için yeterince akıllı gibi geliyor counter2=max. Ve belki de ilk çağrının currentTimeMillisdaha fazla yükü varsa, geçişten daha yavaştır , çünkü her şey henüz JIT derlenmemiştir? Döngüleri diğer sıraya koymak muhtemelen farklı sonuçlar verecektir.
Peter Cordes
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.