Java neden kuyruk özyineleme için hiç bir optimizasyona sahip değil?


92

Okuduklarımdan: Sebep, miras olarak hangi yöntemin gerçekte çağrılacağının belirlenmesi kolay değil.

Ancak, neden Java en azından statik yöntemler için kuyruk özyinelemesi optimizasyonu yapmıyor ve statik yöntemleri derleyici ile çağırmanın uygun bir yolunu uygulamıyor?

Java neden özyineleme için hiç bir desteğe sahip değil?

Burada herhangi bir zorluk olup olmadığından emin değilim.


Önerilen yinelemeyle ilgili olarak, Jörg W Mittag 1 tarafından açıklandığı gibi :

  • Diğer soru TCO hakkında, bu TRE hakkında. TRE, TCO'dan çok daha basittir.
  • Ayrıca, diğer soru JVM'nin JVM'yi derlemek isteyen dil uygulamalarına ne gibi kısıtlamalar getirdiğini soruyor, bu soru JVM spesifikasyonu tarafından değiştirilebildiğinden, JVM tarafından sınırlandırılmayan bir dil olan Java'yı soruyor. Java'yı tasarlayanlarla aynı.
  • Ve son olarak, JVM'de TRE ile ilgili bir kısıtlama bile yoktur, çünkü JVM'nin TRE için ihtiyaç duyduğu tek şey GOTO'ya sahip olmasıdır.

Yapılan çağrıları yapmak için 1 Biçimlendirme eklendi.


26
Eric Lippert'i şöyle ifade etmek gerekirse : "Özellikler ucuz değildir, son derece pahalıdırlar ve sadece kendi maliyetlerini haklılaştırmakla kalmazlar, bu bütçeyle yapabileceğimiz diğer yüzlerce özelliği yapmama fırsatını da haklı çıkarmalıdırlar." Java, kısmen C / C ++ geliştiricilere hitap edecek şekilde tasarlandı ve kuyruk özyineleme optimizasyonu bu dillerde garanti edilmiyor.
Doval,

5
Hatalıysam, beni düzeltin, ancak Eric Lippert, kuyruk özyineleme optimizasyonuna sahip C # tasarımcısı mı?
Bilgilendirilmiş,

1
C # derleyici ekibinde, evet.
Doval

10
Anladığım kadarıyla, JIT bunu yapabilir , belirli koşullar yerine getirildiği takdirde belki. Yani pratikte buna güvenemezsin. Fakat C # olsun ya da olmasın, Eric'in noktası için önemsiz.
Doval,

4
@InstructedA Eğer biraz daha derine inersen, 32-bit JIT derleyicisinde hiç bitmediğini görebilirsin. 64-bit JIT birçok yönden daha yeni ve daha akıllı. Daha da yeni olan deneysel derleyici (hem 32 hem de 64 bit için) daha akıllıdır ve IL'de kuyruk istemesi optimizasyonunu açıkça istemez. Dikkate almanız gereken başka bir nokta var - JIT derleyicilerinin fazla vakti yok. Hız için yoğun şekilde optimize edilmişlerdir - C ++ 'da derlenmesi saatler alabilen uygulamanın en azından (en azından kısmen) birkaç yüz ms içinde IL'den yerli durumuna geçmesi gerekecektir.
Luaan

Yanıtlar:


132

Brian Goetz (Oracle’ın Java Dil Mimarı) tarafından bu videoda açıklandığı gibi :

jdk sınıflarında [...], kimin aradığını bulmak için jdk kütüphane kodu ile çağıran kod arasında yığın karelerinin sayılmasına dayanan bir dizi güvenliğe duyarlı yöntem vardır.

Yığındaki kare sayısını değiştiren herhangi bir şey bunu kırabilir ve bir hataya neden olur. Bunun aptalca bir sebep olduğunu itiraf ediyor ve bu yüzden JDK geliştiricileri bu mekanizmanın yerini almıştır.

Daha sonra bunun bir öncelik olmadığını, ancak kuyruk özyinelemesinden bahseder.

sonunda bitecek.

Not Bu, HotSpot ve OpenJDK için geçerlidir, diğer VM'ler değişiklik gösterebilir.


7
Oldukça kesilmiş ve kurutulmuş bir cevap olduğuna şaşırdım! Fakat gerçekten de Cevap gibi görünüyor - şimdi mümkün değil, eski teknik nedenlerle henüz yapılmamıştı ve bu yüzden şimdi birisinin uygulamak için yeterince önemli olduğuna karar vermesini bekliyoruz.
Brian H

1
Neden bir geçici çözüm uygulamıyorsunuz? Sadece argümanlar için yeni değerlerle çağrılan yöntemin en üstüne sıçrayan kapak altı etiket-goto gibi mi? Bu bir derleme zamanı optimizasyonu olur ve yığın çerçevelerini kaydırması veya güvenlik ihlallerine neden olması gerekmez.
James Watkins,

2
Siz veya buradaki bir başkası daha derine inip bu "güvenliğe duyarlı yöntemlerin" ne olduğunu özellikle belirtebilirseniz, bizim için çok daha iyi olacaktır. Teşekkür ederim!
InformedA

3
@InstructedA - bkz securingjava.com/chapter-three/chapter-three-6.html Java Güvenlik Yöneticisi sistemi Java 2. sürümü dolaylarında nasıl çalıştığını ayrıntılı bir açıklamasını içeren
Jules

3
Geçici bir çözüm, başka bir JVM dili kullanmaktır. Örneğin, Scala bunu çalışma zamanında değil derleyicide yapar.
James Moore

24

Java, zorunlu zorunluluk dillerinin sahip olmadığı aynı sebeple kuyruk çağrısı optimizasyonuna sahip değildir. Zorunlu döngüler, dilin tercih edilen stilidir ve programcı, kuyruk özyinelemesini zorunlu döngülerle değiştirebilir. Karmaşıklık, kullanımı bir tarz olarak önerilmeyen bir özellik için buna değmez.

Programcıların bazen FP tarzında yazmak zorunda kalmaları, aksi takdirde zorunlu dillerdeki zorunlu şeyler sadece son 10 yıl içerisinde moda hale geldi, ancak bilgisayarlar GHz yerine çekirdeklerde ölçeklenmeye başladılar. Şimdi bile, o kadar popüler değil. Bir emirsel döngüyü iş yerinde kuyruk özyinelemeyle değiştirmeyi önerirsem, kod gözden geçirenlerin yarısı gülecek ve diğer yarısı şaşkın bir görünüm verecektir. İşlevsel programlamada bile, üst düzey işlevler gibi diğer yapılar düzgün şekilde uyuşmadıkça, kuyruk özyinelemesinden kaçınırsınız.


36
Bu cevap doğru görünmüyor. Sadece kuyruk özyinelemesine kesinlikle ihtiyaç duyulmaması, bunun işe yaramayacağı anlamına gelmez. Özyinelemeli çözümlerin yinelemeden daha kolay anlaşılması kolaydır, ancak kuyruk çağrılarının eksikliği, özyinelemeli algoritmaların yığını patlatacak büyük sorun boyutları için yanlış olduğu anlamına gelir. Bu doğrulukla ilgilidir, performansla ilgili değil (sadelik için işlem hızı genellikle buna değer). Doğru cevabın da belirttiği gibi, kuyruk çağrısı eksikliği, zorunlu bağnazlığa değil, yığın izlerine dayanan garip bir güvenlik modelinden kaynaklanmaktadır.
04

4
@ amon James Gosling bir keresinde, birden fazla kişi istemediği sürece Java'ya bir özellik ekleyemeyeceğini ve ancak o zaman dikkate alacağını söyledi. Bu nedenle, cevabın bir kısmı gerçekten "her zaman bir fordöngü kullanabilirsiniz " (ve aynı şekilde birinci sınıf işlevler ve nesneler için) olsaydı şaşırmam . Buna "zorunluluk bağnazlığı" diyecek kadar ileri gitmeyecektim ama 1995'te en büyük kaygının muhtemelen Java'nın hızı ve jenerik olmayışının çok yüksek talep göreceğini sanmıyorum.
Doval

7
@ amon, bir güvenlik modeli beni önceden var olan bir dile TCO eklemek için değil, ilk etapta diline göre tasarımlamaması için meşru bir neden olarak vurguluyor. Küçük bir sahne arkası özelliği eklemek için programcının görebildiği önemli bir özelliği atmazsınız. "Neden Java 8'de TCO yok", "Neden Java 1.0'da TCO yok" dan çok farklı bir soru. İkincisi yanıtlıyordum.
Karl Bielefeldt

3
@ rwong, herhangi bir özyinelemeli işlevi yinelemeli bir işleve dönüştürebilirsiniz . (Mümkünse, buraya bir örnek yazdım )
alain

3
@ZanLynx sorunun türüne bağlıdır. Örneğin, Ziyaretçi kalıbı , Genel Kurul ile ilgisi yokken, TCO'dan (yapı ve ziyaretçinin özelliklerine bağlı olarak) faydalanabilir. Durum makinelerinde dolaşmak benzerdir, ancak trambolin kullanan dönüşüm biraz daha doğal görünmektedir (soruna bağlı olarak). Ayrıca, teknik olarak, döngü kullanarak ağaçları uygulayabiliyorsanız, fikir birliğinin özyinelemenin çok daha doğal olduğuna inanıyorum (bu durumda DFS'ye benzer şekilde, TCO ile bile yığın sınırlamaları nedeniyle özyinelemeyi önleyebilirsiniz).
Maciej Piechotka

5

Java yüksek arama optimizasyonuna sahip değildir, çünkü JVM kuyruk çağrıları için bir bayt koduna sahip değildir ( statik olarak bilinmeyen bazı fonksiyon göstergelerine, örneğin bazı kararsız yöntemlerde).

Sosyal (ve belki de teknik) nedenlerden dolayı görünüyor, JVM'ye yeni bir bytecode işlemi eklemek (bu JVM'nin önceki sürümleriyle uyuşmaz hale gelecektir) JVM özelliklerinin sahibi için oldukça zordur.

JVM spesifikasyonuna yeni bir byte kodu eklememenin teknik nedenleri arasında, gerçek hayattaki JVM uygulamalarının çok karmaşık yazılım parçaları olduğu gerçeği yer almaktadır (örneğin, yaptığı birçok JIT optimizasyonu nedeniyle).

Kuyruk bilinmeyen bazı işlevlere yapılan çağrılar, geçerli yığın çerçevesini yenisiyle değiştirmeyi gerektirir ve bu işlem JVM'ye oturmalıdır (bu yalnızca bytecode üreten derleyiciyi değiştirme meselesi değildir).


17
Sorun kuyruk çağrılarıyla ilgili değil, özyinelemeyle ilgili. Ve soru JVM bayt kodu dili ile ilgili değil, tamamen farklı bir dil olan Java programlama dili ile ilgili. Scala, JVM bytecode (diğerleri arasında) derler ve kuyruk özyineleme eleme vardır. JVM'deki uygulamaların tam Uygun Kuyruk Çağrıları vardır. Java 7 (JVM Spec, 3rd Ed.), Yeni bir bytecode ekledi. IBM'in J9 JVM, TCO'yu özel bir byte koduna gerek duymadan bile gerçekleştirmektedir.
Jörg W Mittag

1
@ JörgWMittag: Bu doğru, ama o zaman merak ediyorum, Java programlama dilinin uygun kuyruk çağrılarının olmadığı doğru mu? Java programlama dili olmadığını belirtmek daha doğru olabilir var Spec yetki o o hiçbir şey, doğru kuyruk aramaları olması. (Yani: Ben spec bir şey aslında emin değilim yasakladığı kuyruk aramaları ortadan kaldırarak gelen bir uygulama Bu sadece söz değil..)
ruakh

4

Bir dilin kuyruk çağrısı yapmak için özel bir sözdizimi olmadığı sürece (özyinelemeli veya başka şekilde) ve bir kuyruk çağrısı istendiğinde derleyici cızırtıyor ancak oluşturulamıyorsa, "isteğe bağlı" kuyruk çağrısı veya kuyruk özyinelemesi optimizasyonu bir parçanın bulunduğu durumlar doğuracaktır Kodun bir makinede 100 bayttan az yığın, diğerinde 100.000.000 bayttan fazla yığın gerektirebilir. Bu kadar farklı, sadece nicel değil nitel olarak değerlendirilmelidir.

Makinelerin farklı yığın boyutlarına sahip olması beklenir ve bu nedenle kodun bir makinede çalışması her zaman mümkündür ancak yığını diğerine üfler. Bununla birlikte, genel olarak, yığın yapay olarak daraltılmış olsa bile bir makinede çalışacak olan kod, "normal" yığın boyutlarına sahip tüm makinelerde çalışacaktır. Bununla birlikte, 1.000.000 derinliğe ulaşan bir yöntem, bir makinede diğerine göre değil, kuyruk çağrısı olarak optimize edilmişse, önceki makinede yürütme işlemi, yığını alışılmadık derecede küçük olsa bile büyük olasılıkla çalışacak ve sonuncusu için başarısız olacaktır; .


2

Kuyruk çağrısı özyinelemesinin Java'da kullanılmadığını düşünüyorum, çünkü bunu yapmak yığın izlerini değiştirir ve bu nedenle hata ayıklamayı çok daha zor hale getirir. Java'nın öncelikli hedeflerinden birinin, programcıların kodlarını kolayca debug etmelerine izin vermek olduğunu ve yığın izlemenin bunu özellikle nesneye yönelik programlama ortamında yapması için şart olduğunu düşünüyorum. Aksine, yineleme kullanılabileceğinden, dil komitesi, kuyruk özyinelemeye değmeyeceğini anlamış olmalı.

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.