Derleyiciler, diğer döngü türlerine göre do-while döngüleri için daha iyi kod üretir mi?


89

Zlib sıkıştırma kitaplığında (diğerlerinin yanı sıra Chromium projesinde kullanılan) bir yorum vardır; bu, C'deki bir do-while döngüsünün çoğu derleyicide "daha iyi" kod oluşturduğunu ima eder. İşte göründüğü kod pasajı.

https://code.google.com/p/chromium/codesearch#chromium/src/third_party/zlib/deflate.c&l=1225

Çoğu (veya herhangi) derleyicinin daha iyi (örneğin daha verimli) kod üreteceğine dair herhangi bir kanıt var mı?

Güncelleme: Orijinal yazarlardan biri olan Mark Adler , yorumlarda biraz bağlam verdi .


7
Bu arada, açıklığa kavuşturmak gerekirse, bu Chromium'un bir parçası değil. URL'den de anlaşılacağı gibi, bu bir "3. parti" projesidir ve daha yakından bakarsanız, bu kodun yaygın olarak kullanılan, genel amaçlı bir sıkıştırma kitaplığı olan ZLib'den geldiğini anlayabilirsiniz.

1
The funny "do {}" generates better code--- neyden daha iyi? Komik while () veya sıkıcı olmaktan çok, normal yapmak {}?
n. zamirler 'm.

@ H2CO3 açıklama için teşekkür ederim, soruyu kaynak hakkında daha spesifik olacak şekilde düzenledim.
Dennis

42
Bu yorum 18 yıldan daha uzun bir süre önce Borland ve Sun C derleyicileri döneminde yazılmıştır. Bugün derleyicilerle herhangi bir ilişki tamamen tesadüfi olacaktır. doSadece a'nın aksine bu özel kullanımının whilekoşullu bir daldan kaçınmadığını unutmayın.
Mark Adler

Yanıtlar:


108

Her şeyden önce:

do-whileDöngü, bir döngü whileveya döngü ile aynı şey değildir for.

  • whileve fordöngüler, döngü gövdesini hiç çalıştırmayabilir.
  • Bir do-whiledöngü her zaman döngü gövdesini en az bir kez çalıştırır - ilk koşul denetimini atlar.

Yani mantıksal fark bu. Bununla birlikte, herkes buna kesinlikle bağlı kalmaz. Bu oldukça yaygındır whileveya fordöngüler o bunun yerine daima döngü en az bir defa garanti bile kullanılır. (Özellikle foreach döngüleri olan dillerde .)

Bu nedenle, elma ve portakalları karşılaştırmaktan kaçınmak için, döngünün her zaman en az bir kez çalışacağını varsayarak devam edeceğim. Dahası, döngü sayacı için bir miktar sözdizimi şekeri içeren döngüler forolduklarından, döngülerden tekrar bahsetmeyeceğim while.

Bu yüzden soruyu cevaplayacağım:

Bir whiledöngünün en az bir kez döngü yapması garanti edilirse , do-whilebunun yerine döngü kullanmaktan herhangi bir performans kazancı olur mu ?


A do-while, ilk durum kontrolünü atlar. Yani, değerlendirilecek bir eksi dal ve bir koşul eksik.

Koşulun kontrol edilmesi pahalıysa ve en az bir kez döngü yapmanın garantili olduğunu biliyorsanız, do-whiledöngü daha hızlı olabilir.

Ve bu en iyi ihtimalle bir mikro optimizasyon olarak kabul edilirken, derleyicinin her zaman yapamayacağı bir şeydir: Özellikle derleyici döngünün her zaman en az bir kez gireceğini kanıtlayamadığında.


Başka bir deyişle, bir while döngüsü:

Etkin olarak bununla aynıdır:

Her zaman en az bir kez döngü yapacağınızı biliyorsanız, bu if-ifadesi gereksizdir.


Aynı şekilde, montaj düzeyinde, farklı döngülerin kabaca şu şekilde derlendiği:

do-while döngüsü:

döngü sırasında:

Koşulun kopyalandığına dikkat edin. Alternatif bir yaklaşım şudur:

... ek bir sıçrama için yinelenen kodu değiştirir.

Her iki durumda da normal do-whiledöngüden daha kötüdür .

Bununla birlikte, derleyiciler istediklerini yapabilir. Döngünün her zaman bir kez girdiğini kanıtlayabilirlerse, o zaman işi sizin için yapmış demektir.


Ancak sorudaki belirli örnek için işler biraz tuhaf çünkü boş bir döngü gövdesine sahip. Vücut olmadığı için whileve arasında mantıksal bir fark yoktur do-while.

FWIW, bunu Visual Studio 2012'de test ettim:

  • Boş gövdeyle, aslında whileve için aynı kodu üretir do-while. Yani bu kısım muhtemelen derleyicilerin o kadar iyi olmadığı eski günlerin bir kalıntısıdır.

  • Ancak boş olmayan bir gövdeyle VS2012, koşul kodunun tekrarlanmasını önlemeyi başarır, ancak yine de fazladan bir koşullu sıçrama oluşturur.

Bu yüzden, sorudaki örnek do-whilegenel durumda bir döngünün neden daha hızlı olabileceğini vurgularken , örneğin modern bir derleyiciye herhangi bir fayda sağlamıyor gibi görünmesi ironiktir .

Yorumun kaç yaşında olduğunu düşünürsek, neden önemli olduğunu ancak tahmin edebiliriz. O sırada derleyicilerin gövdenin boş olduğunu fark edememiş olması çok muhtemeldir. (Veya yaptılarsa, bilgiyi kullanmadılar.)


12
Peki durumu bir kez daha kontrol etmek çok büyük bir avantaj mı? Şüphem Çok. Döngüyü 100 kez çalıştırın ve tamamen önemsiz hale gelir.

7
@ H2CO3 Peki ya döngü yalnızca bir veya iki kez çalışıyorsa? Yinelenen durum kodundan artan kod boyutu ne olacak?
Gizemli

6
@Mistik Bir döngü yalnızca bir veya iki kez çalışıyorsa, bu döngü optimize etmeye değmez. Ve artan kod boyutu ... en iyi ihtimalle sağlam bir argüman değildir. Her derleyicinin bunu gösterdiğiniz şekilde uygulaması bir gereklilik değildir. Kendi oyuncak dilim için bir derleyici yazdım ve while döngülerinin derlenmesi döngünün başlangıcına koşulsuz bir sıçrama ile uygulandı, bu nedenle koşul için kod yalnızca bir kez yayınlandı.

30
@ H2CO3 "Bir döngü yalnızca bir veya iki kez çalışıyorsa, bu döngü optimize etmeye değmez." - Naçizane size katılmıyorum. Başka bir döngünün içinde olabilir. Benim son derece optimize edilmiş HPC kodumun tonları bunun gibidir. Ve evet do-while, bir fark yaratır.
Gizemli

29
@ H2CO3 Onu teşvik ettiğimi nerede söyledim? Soru sorar, bir do-whilewhile döngüsünden daha hızlı bir döngüdür. Ve soruyu daha hızlı olabilir diyerek cevapladım. Ne kadar olduğunu söylemedim. Buna değip değmeyeceğini söylemedim. Kimsenin do-while döngülerine dönüştürmeye başlamasını önermedim. Ama basitçe bir optimizasyon olasılığının olduğunu reddetmek, küçük de olsa, bence bu şeylerle ilgilenen ve ilgilenenlere bir kötülük.
Gizemli

24

Çoğu (veya herhangi) derleyicinin daha iyi (örneğin daha verimli) kod üreteceğine dair herhangi bir kanıt var mı?

Fazla değil, bakmak sürece gerçek birlikteliği bir oluşturulan gerçek, spesifik derleyici bir üzerinde belirli bir platform bazı özel optimizasyon ayarları.

Bu muhtemelen onlarca yıl önce (ZLib yazıldığında) endişelenmeye değerdi, ancak bugünlerde kesinlikle değil, gerçek profilleme ile bunun kodunuzdaki bir darboğazı ortadan kaldırdığını görmedikçe .


9
Peki, ifade premature optimizationburada akla geliyor.
James Snell

@JamesSnell aynen. Ve bu, en yüksek puan alan yanıtın desteklediği / teşvik ettiği şeydir.

16
En yüksek puan alan cevabın erken optimizasyonu teşvik ettiğini sanmıyorum. Verimlilikte bir farkın mümkün olduğunu, ancak ne kadar küçük veya önemsiz olursa olsun gösterdiğini iddia ediyorum. Ancak insanlar olayları farklı şekilde yorumlar ve bazıları bunu gerekli olmadığında do-while döngülerini kullanmaya başlamak için bir işaret olarak görebilir (umarım değildir). Her neyse, şimdiye kadarki tüm cevaplardan memnunum. Soruyla ilgili değerli bilgiler sağlarlar ve ilginç tartışmalar yaratırlar.
Dennis

16

Özetle (tl; dr):

OP'lerin kodundaki yorumu biraz farklı yorumluyorum. Gözlemlediklerini iddia ettikleri "daha iyi kod" un asıl çalışmayı döngü "durumuna" taşımaktan kaynaklandığını düşünüyorum. Bununla birlikte, derleyiciye çok özel olduğuna ve yaptıkları karşılaştırmanın, biraz farklı bir kod üretebilmelerine rağmen, aşağıda gösterdiğim gibi çoğunlukla anlamsız ve muhtemelen eski olduğuna tamamen katılıyorum.


Detaylar:

Orijinal yazarın bu do {} whiledaha iyi kod üretme konusundaki yorumuyla neyi kastettiğini söylemek zor , ancak burada ortaya konulandan başka bir yönde spekülasyon yapmak istiyorum - do {} whileve while {}döngüler arasındaki farkın oldukça zayıf olduğuna inanıyoruz ( Mistik dedi), ancak bu kodda daha da "komik" bir şey var ve bu, tüm işi bu çılgın duruma sokuyor ve iç kısmı boş tutuyor ( do {}).

Aşağıdaki kodu gcc 4.8.1 (-O3) üzerinde denedim ve ilginç bir fark yaratıyor -

Derledikten sonra -

Dolayısıyla, ilk döngü 7 komut, ikincisi ise aynı işi yapmaları gerekse bile 6 komut yapar. Şimdi, bunun arkasında bir derleyici zekası olup olmadığını gerçekten söyleyemem, muhtemelen değil ve bu sadece tesadüfi ama bu projenin kullandığı diğer derleyici seçenekleriyle nasıl etkileşime girdiğini kontrol etmedim.


Öte yandan clang 3.3'te (-O3), her iki döngü de bu 5 komut kodunu oluşturur:

Bu da derleyicilerin oldukça farklı olduğunu ve bazı programcıların birkaç yıl önce tahmin edebileceğinden çok daha hızlı ilerlediğini gösteriyor. Aynı zamanda bu yorumun oldukça anlamsız olduğu ve muhtemelen orada olduğu anlamına gelir çünkü hiç kimse hala mantıklı olup olmadığını kontrol etmemiştir.


Sonuç olarak - mümkün olan en iyi kodu optimize etmek istiyorsanız (ve nasıl görünmesi gerektiğini biliyorsanız), bunu doğrudan montajda yapın ve denklemden "orta adam" ı (derleyici) kesin, ancak daha yeni olanı dikkate alın derleyiciler ve daha yeni HW, bu optimizasyonu geçersiz kılabilir. Çoğu durumda, derleyicinin bu düzeydeki işi sizin için yapmasına izin vermek ve büyük şeyleri optimize etmeye odaklanmak çok daha iyidir.

Dikkat edilmesi gereken bir diğer nokta - talimat sayımı (orijinal OP'lerin kodunun bundan sonra olduğu varsayılarak), hiçbir şekilde kod verimliliği için iyi bir ölçüm değildir. Tüm talimatlar aynı şekilde yaratılmadı ve bazıları (örneğin basit reg-to-reg hareketleri) CPU tarafından optimize edildiklerinden gerçekten ucuz. Diğer optimizasyonlar aslında CPU dahili optimizasyonlarına zarar verebilir, bu nedenle sonuçta yalnızca uygun kıyaslama sayılır.


Bir kayıt hareketi kaydediyor gibi görünüyor. mov %rcx,%rsi:) Kodu yeniden düzenlemenin bunu nasıl yapabileceğini görebiliyorum.
Gizemli

@Mistik, mikro optimizasyon konusunda haklısınız. Bazen tek bir talimatı kaydetmenin bile hiçbir değeri yoktur (ve reg-to-reg hareketleri bugün reg yeniden adlandırma ile neredeyse ücretsiz olmalıdır).
Leeor

AMD Bulldozer ve Intel Ivy Bridge'e kadar taşıma yeniden adlandırma uygulanmış gibi görünmüyor. Bu bir sürpriz!
Gizemli

@Mysticial, bunların kabaca bir fiziksel kayıt dosyası uygulayan ilk işlemciler olduğunu unutmayın . Eski sıra dışı tasarımlar, kaydı yeniden sipariş arabelleğine yerleştirir, burada bunu yapamazsınız.
Leeor

3
Görünüşe göre orijinal koddaki yorumu çoğundan farklı yorumlamışsınız ve bu mantıklı. Yorumda "komik olan {} .." yazıyor ama komik olmayan versiyonu karşılaştırdığını söylemiyor. Çoğu kişi do-while ve while arasındaki farkı bilir, bu yüzden tahminimce "komik olan şey {}" bunun için geçerli değil, gösterdiğiniz gibi döngü açma ve / veya fazladan atamanın olmaması buraya.
Abel

10

Bir whiledöngü genellikle do-whilekoşula başlangıç ​​dalına sahip bir döngü olarak derlenir , yani

oysa bir do-whiledöngünün derlenmesi ilk dal olmadan aynıdır. Bundan while(), doğal olarak daha az verimli olduğunu, ilk şubenin maliyeti, ancak yalnızca bir kez ödenen maliyetten görebilirsiniz. [ while,Her yineleme için hem koşullu dallanma hem de koşulsuz dallanma gerektiren saf uygulama yöntemiyle karşılaştırın .]

Bunu söyledikten sonra, gerçekten karşılaştırılabilir alternatifler değiller. Bir whiledöngüyü bir döngüye dönüştürmek acı vericidir do-whileve bunun tersi de geçerlidir. Farklı şeyler yapıyorlar. Ve derleyici ile ne yaptıysa bu durumda birkaç yöntem çağrıları olur tamamen egemen içinde whilekarşı olarakdo-while.


7

Açıklama, kontrol ifadesinin seçimi ile ilgili değildir (do vs. while), bu döngü açma ile ilgilidir !!!

Gördüğünüz gibi, bu bir dize karşılaştırma işlevidir (dizge öğeleri muhtemelen 2 bayt uzunluğundadır), kısayol ve ifadede dört yerine tek bir karşılaştırmayla yazılabilirdi.

Bu ikinci uygulama, her dört öğe karşılaştırmasından sonra dizgi sonu koşulunun tek bir kontrolünü yaptığı için, kesinlikle daha hızlıdır, oysa standart kodlama, karşılaştırma başına bir denetimi içerecektir. Başka bir deyişle, 4 element başına 5 test ve 4 element başına 8 test.

Her neyse, yalnızca dizi uzunluğu dörtten bir katsa veya bir sentinel öğesi varsa çalışır (böylece iki dizginin strendsınırın ötesinde farklı olması garanti edilir ). Oldukça riskli!


Bu ilginç bir gözlem ve şimdiye kadar herkesin gözden kaçırdığı bir şey. Ancak bir derleyicinin bunda etkisi olmaz mı? Başka bir deyişle, hangi derleyicinin kullanıldığına bakılmaksızın her zaman daha verimli olacaktır. Öyleyse neden derleyicilerden bahseden bir yorum var?
Dennis

@Dennis: Farklı derleyicilerin üretilen kodu optimize etmenin farklı yolları vardır. Bazıları kendi kendilerine döngü açmayı (bir ölçüde) yapabilir veya atamaları optimize edebilir. Burada kodlayıcı, derleyiciyi döngü açmaya zorlayarak daha az optimize eden derleyicilerin iyi performans göstermesini sağlar. Yves'in varsayımları konusunda tam olarak haklı olduğunu düşünüyorum, ancak orijinal kodlayıcı olmadan, "komik" açıklamanın ardındaki gerçek düşüncenin ne olduğu biraz gizemini koruyor.
Abel

1
@Abel açıkladığınız için teşekkürler, yorumun arkasındaki (varsayılan) anlamı şimdi daha iyi anlıyorum. Yves, yorumun arkasındaki gizemi çözmeye kesinlikle en çok yaklaştı, ancak Mysticial'ın cevabını en iyi cevapladığını düşündüğüm için kabul edeceğim. Yanlış soruyu sorduğum ortaya çıktı çünkü yorum muhtemelen duruma atıfta bulunurken, döngünün türüne odaklanmam için beni yanılttı.
Dennis

0

Bu arada zamana karşı verimlilik tartışması, bu durumda vücut olmadığı için tamamen anlamsızdır.

ve

kesinlikle eşdeğerdir.

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.