Döngü ters çevirme tekniği nedir?


89

Java için tam zamanında derleyici (JIT) optimizasyon tekniklerinden bahseden bir belgeye bakıyordum. Bunlardan biri "döngü ters çevirme" idi. Ve belge diyor ki:

Normal whilebir do-whiledöngüyü bir döngü ile değiştirirsiniz . Ve do-whiledöngü bir ifcümle içinde ayarlanır . Bu değiştirme, iki daha az sıçramaya yol açar.

Döngü ters çevirme nasıl çalışır ve kod yolumuzu nasıl optimize eder?

Not: Biri bir Java kodu örneğiyle ve JIT'in bunu yerel koda nasıl optimize ettiğini ve modern işlemcilerde neden en uygun olduğunu açıklayabilirse harika olur.


2
Kaynak kodunuza yapacağınız bir şey değil. Yerel kod düzeyinde gerçekleşir.
Marko Topolnik

2
@MarkoTopolnik biliyorum. Ancak JIT'in bunu yerel kod seviyesinde nasıl yaptığını bilmek istiyorum. Teşekkürler.
deneniyor

1
oh harika, bununla ilgili bol miktarda örnek içeren bir wikipedia sayfası var en.wikipedia.org/wiki/Loop_inversion . C örneği, Java'da da geçerlidir.
Benjamin Gruenbaum

Bir süre önce SO ile ilgili sorulardan birinden ilham alarak bu konuda kısa bir araştırma yaptım, belki sonuçlar size yardımcı olabilir: stackoverflow.com/questions/16205843/java-loop-efficiency/…
Adam Siemion

Bu, döngü koşulunun genellikle sona yerleştirildiği yerle aynı şey midir (daha az atlama yapılacağına bakılmaksızın), sadece daha az atlama talimatı (yineleme başına 1'e karşı 2)?
extremeaxe5

Yanıtlar:


108
while (condition) { 
  ... 
}

İş akışı:

  1. durumu kontrol edin;
  2. yanlış ise döngünün dışına atlayın;
  3. bir yineleme çalıştırın;
  4. üste atla.

if (condition) do {
  ...
} while (condition);

İş akışı:

  1. durumu kontrol edin;
  2. yanlışsa, döngünün ötesine atlayın;
  3. bir yineleme çalıştırın;
  4. durumu kontrol edin;
  5. doğruysa 3. adıma atlayın.

Bu ikisini karşılaştırdığınızda, döngüde tam olarak bir adım olması koşuluyla ikincisinin hiç atlama yapmayabileceğini kolayca görebilirsiniz ve genellikle atlama sayısı yineleme sayısından bir az olacaktır. İlki, koşulu kontrol etmek için geri atlamak zorunda kalacak, yalnızca koşul yanlış olduğunda döngüden atlamak zorunda kalacak.

Modern ardışık düzen CPU mimarilerinde sıçramalar oldukça pahalı olabilir: CPU, atlamadan önce kontrollerin yürütülmesini bitirirken, bu atlamanın ötesindeki talimatlar zaten ardışık düzenin ortasındadır. Şube tahmini başarısız olursa, tüm bu işlemler atılmalıdır. Boru hattı cezalandırılırken daha fazla yürütme ertelenir.

Bahsedilen dal tahminini açıklamak : her tür koşullu sıçrama için CPU'nun her biri sonuca ilişkin bir bahis içeren iki talimatı vardır . Örneğin, bir döngünün sonuna " sıfır değilse zıpla, sıfır değil üzerine bahis yap " diyen bir talimat koyarsınız , çünkü atlama sonuncusu hariç tüm yinelemelerde yapılmalıdır. Bu şekilde CPU, atlama komutunun kendisini izleyenler yerine atlama hedefini izleyen talimatlarla işlem hattını pompalamaya başlar.

Önemli Not

Lütfen değil kaynak kodu düzeyinde nasıl optimize bir örnek olarak alın. Bu tamamen yanlış olacaktır çünkü, sorunuzdan da anlaşılacağı üzere, ilk formdan ikinciye dönüşüm, JIT derleyicisinin tamamen kendi başına rutin olarak yaptığı bir şeydir.


51
Sondaki bu not gerçekten çok çok önemli bir şey.
TJ Crowder

2
@AdamSiemion: Verilen do-whilekaynak kodu için oluşturulan bayt kodu alakasızdır, çünkü bunu aslında biz yazmıyoruz. whileDöngüyü yazıyoruz ve derleyicinin ve JIT'in gerekirse / gerektiği takdirde bizim için (döngü ters çevirme yoluyla) onu geliştirmek için işbirliği yapmasına izin veriyoruz.
TJ Crowder

1
Yukarıdakiler için @TJCrowder +1, artı Adam'a not: JIT derleyicisinin optimizasyonlarını düşünürken asla bayt kodunu dikkate almayın . Bayt kodu, çalıştırılan gerçek JIT tarafından derlenen koddan çok Java kaynak koduna daha yakındır. Aslında, modern dillerdeki eğilim, dil spceification'ın bir parçası olarak bayt koduna sahip olmamaktır.
Marko Topolnik

1
Ekstra bilgilendirici olurdu, Önemli not biraz daha açıklandı. Neden tamamen yanlış yönlendirilsin?
arsaKasra

2
@arsaKasra Bu yanlıştır çünkü genel okunabilirlik ve kararlılık, kaynak koddaki optimizasyonları gölgede bırakır. Özellikle JIT'in bunu sizin için yaptığının açığa çıkmasıyla, (çok mikro) optimizasyonu kendi başınıza denememelisiniz.
Radiodef

24

Bu, her zaman en az bir kez yürütülen bir döngüyü optimize edebilir.

Düzenli bir whiledöngü daha sonra her zaman en az bir kez başlangıca geri atlayacak ve sonunda bir kez sona atlayacaktır. Bir kez çalışan basit bir döngü örneği:

int i = 0;
while (i++ < 1) {
    //do something
}  

do-whileÖte yandan bir döngü, ilk ve son atlamayı atlayacaktır. Atlamasız olarak çalışan yukarıdakine eşdeğer bir döngü:

int i = 0;
if (i++ < 1) {
    do {
        //do something
    } while (i++ < 1); 
}

Doğru olduğu için +1, lütfen bir kod örneği eklemeyi düşünün. Böyle bir şey boolean b = true; while(b){ b = maybeTrue();}için boolean b;do{ b = maybeTrue();}while(b);yeterli olacaktır.
Benjamin Gruenbaum

Telaşa gerek yok. Cevabın açılış satırını bir nevi geçersiz kılar, fwiw. :-)
TJ Crowder

@TJ Pekala, girilmemiş bir döngüyü optimize etmeyecek, her iki durumda da bir sıçrama olacaktır.
Keppil

Ah evet. Maalesef verdiğiniz bunu demek için okuyordu olamazdı (o onlara yardım etmediğini ziyade) en az bir kez döngü vermedi döngüler uygulayın. Şimdi seninle. :-)
TJ Crowder

@Keppil Çok sayıda X yinelememiz olması durumunda, X olanlar arasında yalnızca tek bir atlama kaydedeceğimizi muhtemelen açıkça belirtmelisiniz.
Manuel Selva

3

Onların üzerinden geçelim:

whileversiyon:

void foo(int n) {
    while (n < 10) {
       use(n);
       ++n;
    }
    done();
}
  1. Önce koşul doğru değilse test nedip atlarız done();.
  2. Sonra kullanırız ve arttırırız n.
  3. Şimdi duruma geri dönüyoruz.
  4. Durulayın, tekrarlayın.
  5. Koşul artık doğru olmadığında, atlıyoruz done().

do-whileversiyon:

(Unutmayın, bunu aslında kaynak kodunda yapmıyoruz [bu bakım sorunlarını ortaya çıkarır], derleyici / JIT bunu bizim için yapar.)

void foo(int n) {
    if (n < 10) {
        do {
            use(n);
            ++n;
        }
        while (n < 10);
    }
    done();
}
  1. Önce koşul doğru değilse test nedip atlarız done();.
  2. Sonra kullanırız ve arttırırız n.
  3. Şimdi durumu test ediyoruz ve doğruysa geri dönüyoruz.
  4. Durulayın, tekrarlayın.
  5. Koşul artık doğru olmadığında, akarız (atlamayız) done().

Örneğin, var nolmaya başlarsak 9, do-whilesürümde asla atlamayız , oysa whilesürümde başa dönmemiz, testi yapmamız ve sonra doğru olmadığını gördüğümüzde sona geri dönmemiz gerekir. .


3

Döngü ters çevirme, işlemci aynı sonucu daha az komutla elde edebileceğinden performansı artıran bir performans optimizasyon tekniğidir. Bu, çoğunlukla sınır koşullarında performansı iyileştirmelidir.

Bu bağlantı , döngü tersine çevirme için başka bir örnek sağlar. Azaltma ve karşılaştırmanın tek bir komut seti olarak uygulandığı birkaç mimaride, bir for döngüsünü azaltma ve karşılaştırma işlemi ile bir süreye dönüştürmek mantıklıdır.

Wikipedia'nın çok güzel bir örneği var ve bunu burada tekrar açıklıyorum.

 int i, a[100];
  i = 0;
  while (i < 100) {
    a[i] = 0;
    i++;
  }

derleyici tarafından dönüştürülecek

  int i, a[100];
  i = 0;
  if (i < 100) {
    do {
      a[i] = 0;
      i++;
    } while (i < 100);
  }

Bu nasıl performansa dönüşür? İ'nin değeri 99 olduğunda, işlemcinin bir GOTO gerçekleştirmesine gerek yoktur (bu ilk durumda gereklidir). Bu, performansı artırır.

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.