"IF" pahalı mı?


103

Hayatım boyunca, öğretmenimizin o gün tam olarak ne dediğini hatırlayamam ve umarım muhtemelen öğrenirsiniz.

Modül "Veri Yapıları ve Algoritmalar" dır ve bize şu satırlar boyunca bir şeyler söyledi:

ifİfadesi en pahalı [şey] 'dır. [bir şey] [bir şeyi] kaydeder.

Evet, berbat bir hafızam var ve gerçekten çok üzgünüm, ama saatlerdir googling yapıyorum ve hiçbir şey çıkmadı. Herhangi bir fikir?


30
Öğretmenine sormak bir seçenek mi?
Michael Myers

7
Neden öğretmenine e-posta göndermiyorsun? O sırada orada bulunmadıkları sürece (veya öğretmeniniz SO okumuyorsa), SO'daki hiç kimsenin öğretmeninizin ne dediğini bilmesi olası değildir.
Bill Karwin

11
Ve tabii ki zorunlu demiryolu cevabına
bobobobo

If ifadeleri veya özellikle C'den etkilenmiş küme parantezli dillerdeki "?:" İfadeleri, örneğin x86 ve arm işlemcilerinde özel koşullu yürütme talimatları ile uygulanabilir. Bunlar, önceki bir teste göre bazı işlemleri yapan veya yapmayan talimatlardır. Bu mükemmel talimatları kullanmak, koşullu atlama / dallanma / 'git' komutlarına olan ihtiyacı tamamen ortadan kaldırır. Kodda farklı noktalara atlama olmadan (muhtemelen tahmin edilemeyen) düz bir şekilde ilerlediği için program akışını tamamen öngörülebilir hale getirerek bazı durumlarda büyük bir performans artışı.
Cecil Ward

İyi bir derleyici bazen doğru yönde biraz itmeye ihtiyaç duyabilir, böylece aptal olmak ve koşullu atlamaları kullanmak yerine, kodu yeniden düzenleyerek ve muhtemelen bir ifadede veya bir ifadede akıllıca bir aritmetik kullanarak koşullu talimatlar kullanır. : ifade. Asmınızı gerçekten bilmiyorsanız ve örneğin Agner Fog'un optimizasyon kılavuzlarını okumadıkça bununla oynamayın. Derleyiciler bazen ifadeler ya da? : ifadeler kullanılır.
Cecil Ward

Yanıtlar:


190

En düşük seviyede (donanımda), evet, eğer s pahalıysa. Nedenini anlamak için, boru hatlarının nasıl çalıştığını anlamanız gerekir .

Yürütülecek geçerli talimat, tipik olarak komut işaretçisi (IP) veya program sayacı (PC) olarak adlandırılan bir şeyde saklanır ; bu terimler eş anlamlıdır, ancak farklı mimarilerde farklı terimler kullanılmaktadır. Çoğu talimat için, bir sonraki talimatın PC'si sadece mevcut PC artı mevcut talimatın uzunluğudur. Çoğu RISC mimarisi için talimatların tümü sabit uzunluktadır, bu nedenle PC sabit bir miktarda artırılabilir. X86 gibi CISC mimarileri için, komutlar değişken uzunluklu olabilir, bu nedenle talimatı çözen mantık, bir sonraki talimatın yerini bulmak için mevcut talimatın ne kadar uzun olduğunu bulmalıdır.

İçin şube talimatları Ancak yürütülecek sonraki talimat geçerli talimat sonraki konum değil. Dallar, işlemciye bir sonraki talimatın nerede olduğunu söyler. Dallar koşullu veya koşulsuz olabilir ve hedef konum sabit veya hesaplanabilir.

Koşullu ile koşulsuz arasındaki farkı anlamak kolaydır - koşullu dal yalnızca belirli bir koşul geçerliyse alınır (bir sayının diğerine eşit olup olmadığı gibi); Branşman alınmazsa kontrol normal gibi şubeden sonraki talimata geçer. Koşulsuz şubeler için daima şube alınır. Koşullu dallar ififadelerde forve whiledöngülerin kontrol testlerinde görünür. Koşulsuz dallar sonsuz döngülerde, işlev çağrılarında, işlev dönüşlerinde breakve continueifadelerde, kötü şöhretli gotoifadede ve daha pek çok şeyde görünür (bu listeler kapsamlı olmaktan uzaktır).

Şube hedefi bir diğer önemli konudur. Çoğu dalın sabit bir dal hedefi vardır - kodda derleme zamanında sabit olan belirli bir konuma giderler. Bu, ififadeleri, her türden döngüyü, normal işlev çağrılarını ve daha fazlasını içerir. Hesaplanan dallar, çalışma zamanında dalın hedefini hesaplar. Bu, switchifadeleri (bazen), bir işlevden dönenleri, sanal işlev çağrılarını ve işlev işaretçisi çağrılarını içerir.

Peki tüm bunlar performans için ne anlama geliyor? İşlemci ardışık düzeninde bir dallanma talimatının göründüğünü gördüğünde, ardışık düzenini nasıl doldurmaya devam edeceğini bulması gerekir. Program akışında daldan sonra hangi talimatların geldiğini anlamak için iki şeyi bilmesi gerekir: (1) dalın alınıp alınmayacağı ve (2) dalın hedefi. Bunu anlamaya dal tahmini denir ve bu zorlu bir problemdir. İşlemci doğru tahmin ederse, program tam hızda devam eder. İşlemci bunun yerine yanlış tahmin ederse , yanlış şeyi hesaplamak için biraz zaman harcadı. Şimdi ardışık düzenini temizlemesi ve doğru yürütme yolundan gelen talimatlarla yeniden yüklemesi gerekir. Sonuç olarak: büyük bir performans vuruşu.

Bu nedenle, ifadelerin pahalı olmasının nedeni şube yanlış tahminlerinden kaynaklanmaktadır . Bu sadece en düşük seviyededir. Üst düzey kod yazıyorsanız, bu ayrıntılar için endişelenmenize gerek yok. Bunu yalnızca C veya derlemede son derece performans açısından kritik kod yazıyorsanız önemsemelisiniz. Durum böyleyse, daldan arındırılmış kod yazmak, birkaç talimata daha ihtiyaç duyulsa bile, dallara ayrılan koddan daha üstün olabilir. Aşağıdaki gibi şeyler hesaplamak için yapabileceğiniz bazı serin bit twiddling hile vardır abs(), min()ve max()dallanma olmadan.


20
Bu sadece dallardaki yanlış tahminler değil . Dallar ayrıca, derleyici düzeyinde ve ayrıca bir dereceye kadar CPU düzeyinde (elbette, sıra dışı bir CPU için) komutun yeniden sıralanmasını engeller. Yine de güzel ayrıntılı cevap.
jalf

6
Yüksek seviyeli diller nihayetinde düşük seviyeli dillere çevrildiyse ve çok performans odaklı kod yazıyorsanız, ifadelerden kaçınan kod yazarak yine de hiçbir şey kazanmıyor musunuz? Bu kavram daha yüksek seviyeli dillere taşınmıyor mu?
c ..

19

"Pahalı" çok göreceli bir terimdir, özellikle bir " if" ifadesiyle ilişkide , durumun maliyetini de hesaba katmanız gerektiğinden. Bu, birkaç kısa işlemci talimatından uzak bir veritabanına çağrı yapan bir işlevin sonucunu test etmeye kadar her yerde olabilir.

Onun hakkında endişe etmem. Gömülü programlama yapmadığınız sürece, muhtemelen " if" maliyetiyle hiç ilgilenmemelisiniz . Çoğu programcı için , uygulamanızın performansında hiçbir zaman itici bir faktör olmayacaktır.


2
Kesinlikle göreli ... cmp / cond jmp hala birçok işlemcide bir mul'dan daha hızlıdır.
Brian Knoblauch

4
Evet, endişelenmemem gerektiğine katılıyorum. Burada hiçbir şeyi optimize etmeye çalışmıyorum. Ben sadece öğrenmeye ve öğrenmeye çalışıyorum. ;)
pek

15

Dallar, özellikle RISC mimarisi mikroişlemcilerinde, en pahalı talimatlardan bazılarıdır. Bunun nedeni, birçok mimaride, derleyicinin büyük olasılıkla hangi yürütme yolunun alınacağını tahmin etmesi ve bu talimatları bir sonraki yürütülebilir dosyaya koymasıdır, böylece dallanma gerçekleştiğinde bunlar zaten CPU önbelleğinde olacaktır. Şube diğer tarafa giderse, ana belleğe geri dönmeli ve yeni talimatları almalıdır - bu oldukça pahalıdır. Birçok RISC mimarisinde, dallanma (genellikle 2 döngü olan) dışında tüm talimatlar bir döngüdür. Burada büyük bir maliyetten bahsetmiyoruz, bu yüzden endişelenmeyin. Ayrıca derleyici, zamanın% 99'unda yaptığınızdan daha iyi optimize eder: EPIC mimarisi ile ilgili gerçekten harika şeylerden biri (Itanium bir örnektir), dalın her iki tarafındaki talimatları önbelleğe alması (ve işlemeye başlaması), ardından dalın sonucu olduğunda ihtiyaç duymadığı seti atmasıdır. bilinen. Bu, tahmin edilmeyen yol boyunca dallanması durumunda tipik bir mimarinin fazladan bellek erişiminden tasarruf sağlar.


13

Hücre Performansında Dal Eliminasyonuyla Daha İyi Performans makalesine göz atın . Bir başka eğlenceli olan ise , Real Time Collision Detection Blog'daki dalsız seçimler hakkındaki bu gönderi .

Bu soruya yanıt olarak zaten yayınlanan mükemmel yanıtlara ek olarak, "eğer" ifadeleri pahalı düşük düzey işlemler olarak kabul edilse de, daha yüksek düzey bir ortamda dalsız programlama tekniklerini kullanmaya çalıştığınızı hatırlatmak isterim. , bir komut dosyası dili veya bir iş mantığı katmanı (dilden bağımsız olarak) gibi gülünç derecede uygunsuz olabilir.

Çoğu zaman, programlar önce netlik için yazılmalı ve ikinci olarak performans için optimize edilmelidir. Performansın çok önemli olduğu çok sayıda sorun alanı vardır, ancak basit gerçek şu ki çoğu geliştirici, bir işleme motorunun derinliklerinde kullanılmak üzere modüller veya haftalarca çalışan yüksek performanslı bir akışkan dinamiği simülasyonu yazmıyor. Çözümünüzün "sadece çalışması" için en büyük öncelik olduğunda, aklınızdaki son şey, kodunuzdaki bir koşullu ifadenin ek yükünden tasarruf edip edemeyeceğiniz olmalıdır.


Aslında! Ayrıca, çağrıları teşvik eden bir dilde kodlama yaparken (temelde, assembler veya stdlib'siz C dışında herhangi bir şey), normal programlama tekniklerinden gelen boru hattı müdahalesinin, koşullu dallanma ile ilgili soruları bastıracağı da eklenebilir.
Ross Patterson

10

ifkendi içinde yavaş değil . Yavaşlık her zaman görecelidir, bahse girerim hayatım boyunca bir if-ifadesinin "tepesini" hiç hissetmemişsinizdir. Yüksek performanslı bir kod yapacaksanız, yine de dallardan kaçınmak isteyebilirsiniz. Ne yapar ifyavaş işlemci sonra kod önceden yükleme olmasıdır ifbazı sezgisel ve etajer dayalı. ifİşlemci henüz hangi yolun izleneceğini bilmediği için boru hatlarının doğrudan makine kodundaki dal talimatından sonra kodu yürütmesini durduracaktır (boru hatlı bir işlemcide, birden çok talimat araya eklenir ve yürütülür). Yürütülen kodun tersi çalıştırılması gerekebilir (eğer diğer dal alınmışsa branch misprediction, denirse ) veya noopbu yerlere doldurulabilir ki bunun olmaması için.

Eğer ifkötülük, o zaman switchkötülük çok, ve &&, ||de. Endişelenme.


7

Olası en düşük düzeyde if(özellikle uygulamaya özgü tüm ön koşulları hesapladıktan sonra) şunlardan oluşur if:

  • bazı test talimatı
  • test başarılı olursa kodda bir yere atlayın, aksi takdirde ileri doğru ilerleyin.

Bununla ilgili maliyetler:

  • düşük seviyeli bir karşılaştırma - genellikle 1 cpu işlemi, süper ucuz
  • potansiyel sıçrama - pahalı olabilir

Sıçramaların neden pahalı olduğunu öğrenin:

  • cpu tarafından önbelleğe alınmadığı ortaya çıkarsa, bellekte herhangi bir yerde yaşayan bir arbirary koda atlayabilirsiniz - bir sorunumuz var çünkü daha yavaş olan ana belleğe erişmemiz gerekiyor
  • modern CPU'lar dallara karşı ön hazırlık yapar. Başarılı olup olmayacağını tahmin etmeye çalışırlar ve ardışık düzen içinde kod çalıştırırlar, böylece işleri hızlandırırlar. Tahmin başarısız olursa, ardışık düzen tarafından yapılan tüm hesaplamalar geçersiz kılınmalıdır. Bu da pahalı bir işlem

Özetlemek gerekirse:

  • Kanıtlayıcı olabilirse, gerçekten, gerçekten, performansı önemsiyorsanız.
  • Bunu ancak ve ancak gerçek zamanlı ışın izleyici veya biyolojik simülasyon veya benzeri bir şey yazıyorsanız önemsemelisiniz . Gerçek dünyanın çoğunda bunu umursamak için hiçbir neden yok.

Bunu bir sonraki seviyeye taşıyın: iç içe geçmiş ve / veya bileşik if ifadeleri ne olacak? Birisi bunun gibi çok fazla if ifadesi yazarsa, masraf hızlı bir şekilde fark edilebilir hale gelebilir. Ve çoğu geliştiriciye göre, eğer ifadeler bu kadar temel bir işlem gibi görünüyorsa, kıvrımlı koşullu dallanmadan kaçınmak genellikle stilistik bir kaygıya indirgenir. Biçimsel kaygılar hala önemlidir, ancak çoğu zaman o anın sıcağında göz ardı edilmesi gereken ilk endişe olabilirler.
jaydel

7

Modern işlemcilerin uzun yürütme boru hatları vardır, bu da birkaç komutun aynı anda çeşitli aşamalarda yürütüldüğü anlamına gelir. Bir sonraki talimat çalışmaya başladığında her zaman bir talimatın sonucunu bilemeyebilirler. Koşullu bir sıçrama ile karşılaştıklarında (eğer) bazen komut işaretçisinin hangi yöne gitmesi gerektiğini bilmeden önce boru hattının boşalmasını beklemeleri gerekir.

Uzun bir yük treni olarak düşünüyorum. Düz bir çizgide çok fazla kargoyu hızlı bir şekilde taşıyabilir, ancak virajları çok kötü.

Pentium 4 (Prescott), 31 aşamalı meşhur uzun bir boru hattına sahipti.

Wikipedia'da daha fazlası


6

Dallanma, CPU talimatını önceden getirmeyi öldürebilir mi?


"Araştırmam" üzerine, atlama tabloları ve switch ifadeleri için dallanma öğrendim ama if ifadeleri hakkında hiçbir şey öğrenmedim. Bunu biraz detaylandırır mısınız?
pek

IIRC'ye göre, CPU genellikle tek bir olası yürütme yolu boyunca talimatları önceden getirir, ancak tahmin edilen yürütme yolundan bir dallanmaya neden olan bir 'eğer' ifadesi, önceden getirilmiş talimatları geçersiz kılar ve ön deneme yeniden başlamak zorunda kalır.
activout.se

Herhangi bir düzgün işlemci, bir dalın alınıp alınmayacağını tahmin etmeye çalışacak dal tahmin yeteneklerine ve tahmine dayalı talimatı önceden getirmeye (ki bu genellikle oldukça iyidir) sahip olmalıdır. GCC, bir programcının şube tahmin edicileri için ipuçları sağlamasına izin veren C uzantılarına bile sahiptir.
mipadi

2
Dahası, CPU genellikle gelecek talimatları erken uygulamaya başlamak için ileriye bakar (sadece onları önceden getirmekle kalmaz) ve derleyici komutları yeniden sıralamayı dener ve bu, dallar arasında tehlikeli hale gelir, böylece çok fazla dalda talimat planlamasını gerçekten öldürebilirsiniz. Performansı düşüren.
jalf

6

Ayrıca, bir döngü içinde olmanın mutlaka çok pahalı olmadığını unutmayın .

Modern CPU, bir if ifadesinin ilk ziyaretinde, "if-body" öğesinin alınacağını varsayar (veya başka bir şekilde söylenir: aynı zamanda bir döngü gövdesinin birden çok kez alınacağını varsayar) (*). İkinci ve sonraki ziyaretlerde, o (CPU) Şube Geçmişi Tablosuna bakabilir ve koşulun son seferde nasıl olduğunu görebilir (doğru muydu? Yanlış mıydı?). Son sefer yanlışsa, spekülatif yürütme if'in "else" e veya döngünün ötesine geçecektir.

(*) Kural aslında " ileri dal alınmadı, geriye doğru dal alınmadı " dır . Bir if ifadesinde, koşul yanlış olarak değerlendirilirse (hatırlayın: CPU yine de dallanma / atlama yapmayacağını varsayar), ancak bir döngüde yalnızca [ileri] sıçrama vardır ( if gövdesinden sonraki noktaya ) Döngüden sonraki pozisyona doğru bir ileri dal olabilir (alınmayacaktır) ve tekrar üzerine geriye doğru bir dal olabilir (alınacak).

Bu aynı zamanda, bir sanal işleve veya işlev işaretçi çağrısına yapılan bir çağrının, çoğu kişinin sandığı kadar kötü olmamasının nedenlerinden biridir ( http://phresnel.org/blog/ )


5

Pek çok kişinin işaret ettiği gibi, koşullu dallar modern bir bilgisayarda çok yavaş olabilir.

Bununla birlikte, if ifadelerinde yaşamayan çok sayıda koşullu dal vardır, derleyicinin ne bulacağını her zaman söyleyemezsiniz ve temel ifadelerin ne kadar süreceği konusunda endişelenmek neredeyse her zaman yanlış bir şeydir. yapmak. (Derleyicinin güvenilir bir şekilde ne üreteceğini söyleyebilirseniz, iyi bir optimizasyon derleyicisine sahip olmayabilirsiniz.)


4

Bunun atıfta bulunduğunu hayal edebildiğim tek şey, bir ififadenin genellikle dallanma ile sonuçlanabileceği gerçeğidir . İşlemci mimarisinin özelliklerine bağlı olarak, dallar boru hattı duraklamalarına veya optimal olmayan durumlara neden olabilir.

Ancak, bu son derece duruma özgüdür - çoğu modern işlemcinin dallanmanın olumsuz etkilerini en aza indirmeye çalışan dal tahmin yetenekleri vardır. Başka bir örnek, ARM mimarisinin (ve muhtemelen diğerlerinin) koşullu mantığı nasıl işleyebileceğidir - ARM, komut düzeyinde koşullu yürütmeye sahiptir, bu nedenle basit koşullu mantık, dallanmaya neden olmaz - koşullar karşılanmazsa, talimatlar yalnızca NOP olarak yürütülür.

Tüm bunlar - bu tür şeyler hakkında endişelenmeden önce mantığınızı düzeltin. Yanlış kod, alabildiğiniz kadar optimize edilmemiştir.


ARM'nin koşullu talimatlarının ILP'yi engellediğini duydum, bu yüzden sorunu çözüyor olabilirler.
JD

3

CPU'lar derinlemesine ardışık düzenlenmiştir. Herhangi bir dallanma talimatı (if / for / while / switch / etc), CPU'nun daha sonra hangi komutu yükleyip çalıştıracağını gerçekten bilmediği anlamına gelir.

CPU ya ne yapılacağını bilmek için beklerken durur ya da CPU tahmin eder. Daha eski bir CPU olması durumunda veya tahmin yanlışsa, doğru talimatı yüklerken ve yüklerken bir ardışık düzen durması yaşamanız gerekir. CPU'ya bağlı olarak bu, 10-20 komut değerinde duraklamaya kadar çıkabilir.

Modern CPU'lar, iyi dal tahmini yaparak ve aynı anda birden fazla yolu çalıştırarak ve yalnızca gerçek olanı koruyarak bundan kaçınmaya çalışır. Bu çok yardımcı olur, ancak yalnızca bir yere kadar gidebilir.

Sınıfta bol şans.

Ayrıca, gerçek hayatta bunun için endişelenmeniz gerekiyorsa, muhtemelen işletim sistemi tasarımı, gerçek zamanlı grafikler, bilimsel hesaplama veya benzer şekilde CPU'ya bağlı bir şey yapıyorsunuzdur. Endişelenmeden önce profil.


2

Programlarınızı, açıkçası verimsiz olmayan en net, en basit ve en temiz şekilde yazın. Bu, en pahalı kaynağı en iyi şekilde kullanır, siz. Programı yazmak veya daha sonra hata ayıklamak (anlamak gerektirir). Performans yeterli değilse ölçünDarboğazların nerede olduğunu ve bunların nasıl azaltılacağını görün. Sadece çok nadir durumlarda, bunu yaparken bireysel (kaynak) talimatlar hakkında endişelenmeniz gerekecektir. Performans, ilk satırda doğru algoritmaları ve veri yapılarını seçmek, dikkatli programlama, yeterince hızlı bir makine elde etmekle ilgilidir. İyi bir derleyici kullanın, modern bir derleyicinin yaptığı yeniden yapılandırma kodlarını görünce şaşıracaksınız. Kodun performans için yeniden yapılandırılması bir tür son çare önlemidir, kod daha karmaşık hale gelir (dolayısıyla daha hatalıdır), değiştirilmesi zorlaşır ve dolayısıyla her açıdan daha pahalıdır.



0

Bu tartışmayı bir zamanlar bir arkadaşımla yapmıştım. Çok saf bir daire algoritması kullanıyordu, ama benimkinden daha hızlı olduğunu iddia etti (Sadece dairenin 1 / 8'ini hesaplayan tür) çünkü benim if kullandı. Sonunda, if ifadesi sqrt ile değiştirildi ve bir şekilde bu daha hızlıydı. Belki de FPU'da yerleşik sqrt olduğu için?


-1

ALU kullanımı açısından en pahalı olanı? Karşılaştırılacak değerleri depolamak için CPU kayıtlarını kullanır ve if ifadesi her çalıştırıldığında değerleri almak ve karşılaştırmak zaman alır.

Bu nedenle, bunun bir optimizasyonu, döngü çalıştırılmadan önce bir karşılaştırma yapmak ve sonucu bir değişken olarak saklamaktır.

Sadece eksik kelimelerini yorumlamaya çalışıyorum.

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.