Bayt koduna karşı makine kodunun derlenmesi


13

Geçici bir bayt kodu (Java gibi) üreten derleme, makine koduna "tamamen" gitmek yerine, genellikle daha az karmaşıklık içerir (ve bu nedenle daha az zaman alır)?

Yanıtlar:


22

Evet, Java bayt kodunu derlemek makine kodunu derlemekten daha kolaydır. Bunun nedeni kısmen hedeflenecek tek bir biçim olmasıdır (Mandrill'in belirttiği gibi, bu sadece derleyici karmaşıklığını azaltır, derleme süresini değil), kısmen JVM, gerçek CPU'lardan çok daha basit bir makine ve programlamak için daha kolay - çünkü Java diline göre, çoğu Java işlemi çok basit bir şekilde tam bir bayt kod işlemiyle eşleşir. Bir başka çok önemli sebep, pratikte hayıroptimizasyon gerçekleşir. Neredeyse tüm verimlilik endişeleri JIT derleyicisine (veya bir bütün olarak JVM'ye) bırakılır, böylece normal derleyicilerin tüm orta ucu kaybolur. Temel olarak AST'den bir kez geçebilir ve her düğüm için hazır bayt kodu dizileri oluşturabilir. Yöntem tabloları, sabit havuzlar vb. Üretmenin bazı "idari yükü" vardır, ancak bu, örneğin LLVM'nin karmaşıklıklarıyla karşılaştırıldığında hiçbir şey değildir.


"... orta ucunu ..." yazdınız. Şunu mu demek istediniz: "... middle to end of ..."? Ya da belki "... orta kısmı ..."?
Julian A.

6
@Julian "orta uç" anlambilimine saygısız "ön uç" ve "arka uç" ile benzer bir şekilde gerçek bir terim :)

7

Bir derleyici, basitçe okunabilir 1 metin dosyalarını alan ve bir makine için ikili talimatlara çeviren bir programdır . Bir adım geri atarsanız ve sorunuzu bu teorik açıdan düşünürseniz, karmaşıklık kabaca aynıdır. Ancak, daha pratik düzeyde, bayt kodu derleyicileri daha basittir.

Bir programı derlemek için hangi geniş adımlar atılmalı?

  1. Kaynak kodu tarama, ayrıştırma ve doğrulama.
  2. Kaynağı soyut bir sözdizimi ağacına dönüştürme.
  3. İsteğe bağlı: dil belirtimi izin veriyorsa AST'yi işleyin ve geliştirin (örn. Ölü kodu kaldırma, yeniden sıralama işlemleri, diğer optimizasyonlar)
  4. AST'yi bir makinenin anlayacağı bir biçime dönüştürmek.

İkisi arasında sadece iki gerçek fark var.

  • Genel olarak, birden fazla derleme birimine sahip bir program, makine koduna derlenirken bağlantı gerektirir ve genellikle bayt koduna sahip değildir. Bağlamanın bu soru bağlamında derlemenin bir parçası olup olmadığı konusunda kıllar bölünebilir. Eğer öyleyse, bayt kodu derlemesi biraz daha basit olacaktır. Bununla birlikte, bağlantının karmaşıklığı, birçok bağlantı kaygısının VM tarafından ele alındığı zamanlarda çalışma zamanında oluşur (aşağıdaki notuma bakın).

  • Bayt kodu derleyicileri optimizasyon yapma eğilimindedir çünkü VM bunu anında daha iyi yapabilir (JIT derleyicileri günümüzde VM'lere oldukça standart bir eklentidir).

Bundan, bayt kodu derleyicilerinin çoğu optimizasyonun ve tüm bağlantıların karmaşıklığını atlayabileceği ve her ikisini de VM çalışma zamanına erteleyeceği sonucuna varıyorum. Bayt kodu derleyicileri pratikte daha basittir, çünkü makine kodu derleyicilerinin kendileri tarafından üstlendiği VM'ye birçok karmaşıklık getirirler.

1 Ezoterik dilleri saymıyor


3
Optimizasyonları görmezden gelmek aptalca. Bu "isteğe bağlı adımlar" çoğu derleyicinin kod tabanını, karmaşıklığını ve derleme süresini büyük ölçüde oluşturur.

Uygulamada, bu doğru. Burada akademisyeni cilaladım, cevabımı güncelledim

Optimizasyonları gerçekten yasaklayan herhangi bir dil belirtimi var mı? Bazı dillerin zorlaştığını anlıyorum, ancak herhangi bir şeyin başlamasına izin vermiyor musunuz?
Davidmh

@Davidmh Onları yasaklayan özelliklerin farkında değilim . Anladığım kadarıyla, çoğu derleyiciye izin verildiğini, ancak herhangi bir ayrıntıya girmediğini söylüyor. Her uygulama farklıdır çünkü birçok optimizasyon genel olarak CPU, işletim sistemi ve hedef mimarinin ayrıntılarına dayanır. Bu nedenle, bir bayt kod derleyicisinin optimizasyonu ve bunun yerine altta yatan mimariyi bilen VM'ye yönlendirmesi daha az olasıdır.

4

Derleme her zaman genel sanal makine kodu Java için derleyici tasarımı basitleştirir söyleyebilirim. Bu, kodu yalnızca bir kez derlemeniz gerektiği ve herhangi bir plataform üzerinde çalışacağı anlamına gelir (her makinede derlemek yerine). Sanal makineyi standart bir makine gibi düşünebileceğiniz için derleme süresinin daha düşük olacağından emin değilim.

Öte yandan, her makine "sanal bayt kodunu" (java kodu derlemesinden kaynaklanan sanal makine kodu) yorumlayabilmesi, gerçek makine koduna çevirip çalıştırabilmesi için Java Sanal Makinesi yüklü olmalıdır. .

Imo bu çok büyük programlar için iyidir, ancak küçük olanlar için çok kötüdür (çünkü sanal makine hafıza kaybıdır).


Anlıyorum. Bayt kodunu standart makineye (yani JVM) eşlemenin karmaşıklığının, kaynak kodun fiziksel bir makineye eşlenmesi ile eşleşeceğini ve bayt kodunun daha kısa derleme süresine neden olacağını düşünmek için hiçbir neden bırakmayacağını mı düşünüyorsunuz?
Julian A.

Ben öyle demedim. Java kodunu bayt koduna (Sanal Makine Birleştiricisi olan) eşleme, kaynak kodunu (Java) fiziksel makine koduna eşleme ile eşleşeceğini söyledim.
Mandrill

3

Derlemenin karmaşıklığı büyük ölçüde kaynak dil ile hedef dil arasındaki anlamsal boşluğa ve bu boşluğu doldururken uygulamak istediğiniz optimizasyon seviyesine bağlıdır.

Örneğin, Java kaynak kodunu JVM bayt koduna derlemek nispeten basittir, çünkü Java'nın hemen hemen doğrudan JVM bayt kodunun bir alt kümesiyle eşlenen bir çekirdek Java alt kümesi vardır. Bazı farklılıklar var: Java'nın döngüleri var ama hayır GOTO, JVM'nin var GOTOama döngüleri yok, Java'nın jenerikleri var, JVM yok, ancak bunlar kolayca ele alınabilir (döngülerden koşullu atlamalara dönüşüm önemsizdir, tür silinmesi biraz daha azdır ancak yine de yönetilebilir). Başka farklılıklar var ama daha az şiddetli.

Ruby kaynak kodunu JVM bayt koduna derlemek çok daha karmaşıktır (özellikle Java 7'de daha önce invokedynamicve MethodHandlesdaha doğrusu JVM spesifikasyonunun 3. Sürümünde tanıtılmıştır). Ruby'de yöntemler çalışma zamanında değiştirilebilir. JVM'de, çalışma zamanında değiştirilebilecek en küçük kod birimi bir sınıftır, bu nedenle Ruby yöntemlerinin JVM yöntemlerine değil JVM sınıflarına derlenmesi gerekir. Ruby yöntem dağıtımı JVM yöntem dağıtımıyla eşleşmiyor ve daha önce invokedynamic, kendi yöntem dağıtım mekanizmanızı JVM'ye enjekte etmenin bir yolu yoktu. Ruby'nin süreklilikleri ve ortak programları var, ancak JVM bunları uygulamak için gerekli olanaklardan yoksundur. (JVM'lerGOTO JVM'nin, süreklilikleri uygulayacak kadar güçlü olacak tek kontrol akışı ilkelinin istisnalar ve her ikisi de son derece ağır olan coroutines ipliklerini uygulamak için tek kontrol akışı ilkesi, çok hafif ol.

Ruby kaynak kodunu Rubinius bayt koduna veya YARV bayt koduna derleyen OTOH, yine önemsizdir, çünkü bunların her ikisi de Ruby için bir derleme hedefi olarak tasarlanmıştır (Rubinius ayrıca CoffeeScript ve diğer ünlü Fantezi gibi diğer diller için de kullanılmıştır) .

Aynı şekilde, x86 yerel kodunu JVM bayt koduna derlemek kolay değildir, yine oldukça büyük bir anlamsal boşluk vardır.

Haskell başka iyi bir örnektir: Haskell ile, yerli x86 makine kodu üreten birkaç yüksek performanslı, endüstriyel güç üretime hazır derleyici vardır, ancak bu tarihe kadar, JVM veya CLI için çalışan bir derleyici yoktur, çünkü semantik boşluk o kadar büyük ki onu köprülemek çok karmaşık. Bu, yerel makine koduna derlemenin JVM veya CIL bayt koduna derlemekten daha az karmaşık olduğu bir örnektir . Bunun nedeni, yerel makine kodunun, GOTOyöntem çağrıları veya özel durumlar gibi daha yüksek düzey ilkelleri kullanmaktan daha kolay "zorla" yapılabilen çok daha düşük düzeyli ilkellere ( , işaretçiler, ...) sahip olmasıdır.

Yani, hedef dil ne kadar yüksek olursa, derleyicinin karmaşıklığını azaltmak için kaynak dilin anlambilimine daha yakın olması gerektiği söylenebilir.


0

Pratikte, günümüzde çoğu JVM, JIT derlemesi yapan çok karmaşık bir yazılımdır (bu nedenle bayt kodu, JVM tarafından dinamik olarak makine koduna çevrilir).

Bu nedenle, Java kaynak kodundan (veya Clojure kaynak kodundan) JVM bayt koduna derleme gerçekten daha basit olsa da, JVM'nin kendisi makine koduna karmaşık çeviri yapıyor.

JVM içindeki bu JIT çevirisinin dinamik olması, JVM'nin bayt kodunun en alakalı kısımlarına odaklanmasını sağlar. Pratik olarak konuşursak, çoğu JVM, JVM bayt kodunun en sıcak kısımlarını (örneğin en çok adlandırılan yöntemler veya en çok çalıştırılan temel bloklar) daha fazla optimize eder.

JVM + Java'nın bytecode derleyicisine kombine karmaşıklığının, vaktinden önce derleyicilerin karmaşıklığından önemli ölçüde daha az olduğundan emin değilim .

Ayrıca, çoğu geleneksel derleyicinin ( GCC veya Clang / LLVM gibi ) giriş C (veya C ++ veya Ada, ...) kaynak kodunu, aşağıdakine benzer bir dahili temsile ( GCC için Gimple , Clang için LLVM ) dönüştürdüğüne dikkat edin. bazı bayt kodları. Daha sonra, dahili temsillerin (önce onu optimize etme, yani GCC optimizasyonlarının çoğu geçişi) Gimple'ı girdi olarak alıyor ve Gimple'ı çıktı olarak üretiyor; daha sonra montajcı veya makine kodunu yayıyor) nesne koduna dönüştürüyorlar.

BTW, en son GCC (özellikle libgccjit ) ve LLVM altyapısı ile, bunları başka bir (veya kendi) dilinizi dahili Gimple veya LLVM temsillerine derlemek için kullanabilir, ardından orta uç ve arka Bu derleyicilerin uç kısımları.

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.