C ++ 'ın JIT ile JVM veya CLR'den daha hızlı olabileceği iddiasını ne desteklemektedir? [kapalı]


119

SE'de yinelenen bir tema, pek çok soruda fark ettim, C ++ 'nın Java gibi daha yüksek seviyeli dillerden daha hızlı ve / veya daha verimli olduğu iddiası. Bunun karşıt argümanı, modern JVM veya CLR'nin JIT ve benzerleri sayesinde daha verimli olması ve gittikçe artan sayıda görev için devam etmesi ve C ++ ' ın sadece ne yaptığınızı ve neden bir şeyi yaptığınızı bilmeniz durumunda daha verimli olduğudur. performans artışlarını hak edecektir. Bu açık ve mükemmel mantıklı.

Ben üzere (böyle bir şey varsa ...) temel bir açıklama bilmek istiyorum neden ve nasıl JVM veya CLR daha ++ hızlı C olan belirli görevleri? Basitçe C ++ 'ın makine kodunda derlendiğinden, JVM veya CLR'nin hala çalışma zamanında JIT derlemesinin genel giderine sahip olması nedeniyle mi?

Konuyu araştırmaya çalıştığımda, bulduğum tek şey, C ++ 'ın yüksek performanslı bilgi işlem için tam olarak nasıl kullanılabileceğini anlama konusunda hiçbir ayrıntılı bilgi olmadan yukarıda belirtilenlerle aynı argümanlar.


Performans ayrıca programın karmaşıklığına da bağlıdır.
pandu

23
"C ++, yalnızca ne yaptığınızı ve işlerin neden belirli bir şekilde yapılmasının performans artışlarını hak edeceğini bilirseniz daha verimlidir." Bunun sadece bir bilgi meselesi olmadığını, geliştirici zaman meselesi olduğunu söyleyerek. Optimizasyonu en üst düzeye çıkarmak her zaman verimli değildir. Bu nedenle, Java ve Python gibi daha yüksek seviyeli dillerin (diğer nedenlerin yanı sıra) mevcut olması - bir programcının çok iyi ayarlanmış bir optimizasyon pahasına belirli bir görevi gerçekleştirmek için programlama yapmak zorunda kaldığı süreyi azaltmak.
Joel Cornett

4
@Joel Cornett: Tamamen katılıyorum. Java'da kesinlikle C ++ 'dan daha üretkenim ve gerçekten hızlı kod yazmam gerektiğinde sadece C ++' ı düşünüyorum. Öte yandan, zayıf yazılmış C ++ kodunun gerçekten yavaş olduğunu gördüm: C ++ vasıfsız programcıların elinde daha az yararlı.
Giorgio

3
Bir JIT tarafından üretilebilecek herhangi bir derleme çıkışı C ++ tarafından üretilebilir, ancak C ++ 'nın üretebileceği kod mutlaka bir JIT tarafından üretilmeyebilir. Bu nedenle C ++ 'ın yetenekleri ve performans özellikleri, herhangi bir üst seviye dilinkilerin üstünlüğüdür. QED
tylerl

1
@Doval Teknik olarak doğru, ancak kural olarak, bir yandan programın performansını etkileyen olası çalışma zamanı faktörlerini bir elinizde sayabilirsiniz. Genellikle ikiden fazla parmak kullanmadan. Bu yüzden en kötü durumda birden fazla ikili dosya gönderiyorsunuz ... bunun dışında yapmanız gerekmediği anlaşılıyor çünkü potansiyel hızlanma ihmal edilebilir, bu yüzden kimse rahatsız etmiyor.
tylerl

Yanıtlar:


200

Her şey hafıza ile ilgili (JIT ile değil). JIT 'C'ye göre avantajı, CPU BTB'nin zaten çok çalıştığı bir şey olan, sanal veya sanal olmayan aramaları satır içi olarak optimize etmekle sınırlıdır.

Modern makinelerde RAM'e erişim gerçekten yavaştır (CPU'nun yaptığı herhangi bir şeye kıyasla), yani önbellekleri mümkün olduğu kadar kullanan uygulamalar (daha az bellek kullanıldığında daha kolay), bilgisayardan yüz kat daha hızlı olabilir. yok. Java'nın C ++ 'dan daha fazla bellek kullandığı ve önbellekten tamamen yararlanan uygulamalar yazmayı zorlaştırdığı birçok yol var:

  • Her nesne için en az 8 baytlık bir bellek yükü vardır ve birçok yerde (yani standart koleksiyonlar) ilkel maddeler yerine nesnelerin kullanılması gerekir veya tercih edilir.
  • Dizeler iki nesneden oluşur ve ek yükü 38 bayttır
  • UTF-16 dahili olarak kullanılır, bu da her ASCII karakterinin bir yerine iki bayta ihtiyaç duyduğu anlamına gelir (Oracle JVM yakın zamanda saf ASCII dizeleri için bunu önlemek için bir optimizasyon başlattı).
  • Toplam referans türü (yapı gibi) yoktur ve sırayla toplam referans türü dizisi yoktur. Bir Java nesnesi veya bir Java nesnesi dizisi, C-yapılarına ve dizilere kıyasla çok zayıf L1 / L2 önbellek konumuna sahiptir.
  • Java jenerik türleri, tip örneklemesine kıyasla önbellek yerinin zayıf olduğu tür silme özelliğini kullanır.
  • Nesne tahsisi opaktır ve her nesne için ayrı ayrı yapılması gerekir, bu nedenle bir uygulamanın verilerini kasıtlı olarak önbellek dostu bir şekilde yerleştirmesi ve yine de yapılandırılmış veri olarak ele alması imkansızdır.

Diğer bazı bellekler - önbellekle ilgili olmayan faktörler:

  • Yığın tahsisi yoktur, bu nedenle birlikte çalıştığınız tüm ilkel olmayan verilerin öbek üzerinde olması ve çöp toplama işleminden geçmesi gerekir (bazı JIT'ler bazı durumlarda perde arkasında yığın tahsisi yapar).
  • Toplu başvuru türü olmadığından, toplu başvuru türlerinden geçen hiçbir yığın yoktur. (Vektör argümanlarının verimli bir şekilde iletildiğini düşünün)
  • Çöp toplama işlemi L1 / L2 önbellek içeriğine zarar verebilir ve GC dünyayı durdurma etkileşimini durdurur.
  • Veri türleri arasında dönüştürme yapmak her zaman kopyalama gerektirir; bir soketten aldığınız bir sürü bayta işaretçi alamaz ve bunları bir şamandıra olarak yorumlayabilirsiniz.

Bunlardan bazıları tradeoffettir (manuel bellek yönetimi yapmak zorunda kalmamak çoğu insan için çok fazla performans vermekten vazgeçmeye değerdir ), bazıları muhtemelen Java'yı basit tutmaya çalışmanın bir sonucudur ve bazıları tasarım hatalarıdır (muhtemelen sadece göz önünde olsa bile) yani UTF-16, Java oluşturulduğunda sabit uzunluklu bir kodlamadı, bu da onu seçmeyi çok daha anlaşılır hale getiriyordu.

Java / JVM için bu değişimlerin çoğunun C # / CIL'ye göre çok farklı olduğunu belirtmekte fayda var. .NET CIL'de başvuru türü yapılar, yığın tahsisi / geçişi, yapıların paketlenmiş dizileri ve tür örneklemeli jenerikler bulunur.


37
+1 - genel olarak, bu iyi bir cevap. Ancak, "yığın ayırma yok" madde işaretinin tamamen doğru olduğundan emin değilim. Java JIT'leri genellikle mümkün olduğunda yığın tahsisine izin vermek için kaçış analizi yapar - belki de söylemeniz gereken Java dilinin programcının bir nesnenin yığın tahsisine karşı yığın tahsis edilmesine karar vermesine karar vermesine izin vermemesidir. Ek olarak, bir nesil çöp toplayıcı (tüm modern JVM'lerin kullandığı) kullanımdaysa, "yığın tahsisi", C ++ ortamında olduğundan tamamen farklı bir şey (tamamen farklı performans özelliklerine sahip) anlamına gelir.
Daniel Pryden

5
Başka iki şey olduğunu düşünürdüm ama çoğunlukla daha yüksek seviyedeki şeylerle çalışıyorum, bu yüzden yanlış olup olmadığımı söyleyin. Bellekte gerçekte neler olup bittiğine ve makine kodunun gerçekte nasıl çalıştığına dair genel bir farkındalık geliştirmeden C ++ 'ı gerçekten yazamazsınız, oysa komut dosyası ya da sanal makine dilleri tüm bunları dikkatinizden uzaklaştırır. Ayrıca, bir VM'de ya da yorumlanmış bir dilde, hangi temel kütüphane yazarlarının aşırı belirli bir senaryo için en iyi duruma getirebileceklerine güvendiğinizden, işlerin nasıl yürüdüğü üzerinde çok daha iyi bir denetime sahipsiniz.
Erik,

18
+1. Ekleyeceğim bir şey daha var (ancak yeni bir cevap göndermeyi istemiyorum): Java’daki dizi indekslemesi her zaman sınır kontrolünü içerir. C ve C ++ ile durum böyle değil.
riwalk

7
Bu Java'nın yığın ayırma (nedeniyle iç havuzlama ve şeyler) C ++ ile bir naif sürümü önemli ölçüde daha hızlı olduğunu belirtmek gerekir, ama C ++ bellek ayırma olabilir Eğer ne yaptığınızı biliyorsanız önemli ölçüde daha iyi olacak.
Brendan Long,

10
@BrendanLong, true .. ancak yalnızca bellek temizse - bir uygulama bir süre çalıştığında, belleği boşaltmak, sonlandırıcıları çalıştırmak gibi işlemleri önemli ölçüde yavaşlatan GC'nin gerekmesi nedeniyle bellek ayırma işlemi yavaşlar kompakt. Kriterlere fayda sağlayan bir takas ama (IMHO) genel olarak uygulamaları yavaşlatıyor.
gbjbaanb

67

Basitçe C ++ montaj / makine kodunda derlenirken Java / C # hala çalışma zamanında JIT derlemesinin işlem yüküne sahip mi?

Kısmen, ancak genel olarak, kesinlikle harika bir JIT derleyicisi olduğu varsayımıyla, uygun C ++ kodu hala İKİ ana nedenlerle Java kodundan daha iyi performans gösterme eğilimindedir:

1) C ++ şablonları hem kod yazmak için daha iyi olanaklar sağlamak jenerik VE verimli . Şablonlar, C ++ programlayıcısına SIFIR çalışma zamanı ek yükü olan çok faydalı bir soyutlama sağlar. (Şablonlar temelde derleme zamanı ördek tiplendirmesidir.) Buna karşın, Java generics ile elde edebileceğiniz en iyi şey, temelde sanal işlevlerdir. Sanal işlevler her zaman bir çalışma zamanı ek yüküne sahiptir ve genellikle satır içi yapılamaz.

Genel olarak, Java, C # ve hatta C dahil olmak üzere birçok dil verimlilik ve genellik / soyutlama arasında seçim yapmanızı sağlar. C ++ şablonları ikinizi de verir (daha uzun derleme süreleri pahasına).

2) C ++ standardının, derlenmiş bir C ++ programının ikili düzeni hakkında söylenecek çok fazla şey olmaması, C ++ derleyicilere Java derleyicisinden çok daha fazla hareket alanı sağlayarak daha iyi optimizasyonlara izin verir (bazen hata ayıklamada daha zor olmak üzere). ) Aslında, Java dili spesifikasyonunun niteliği, bazı alanlarda performans cezasını zorlamaktadır. Örneğin, Java'da bitişik bir Nesneler dizisine sahip olamazsınız. Yalnızca bitişik bir Nesne işaretçisi dizisine sahip olabilirsiniz.(referanslar), yani, Java’daki bir dizi üzerinde yineleme yapmak her zaman dolaylı maliyete yol açar. Bununla birlikte, C ++ 'ın semantiği, bitişik dizileri etkinleştirir. Diğer bir fark, C ++ 'ın nesnelerin yığında tahsis edilmesine izin vermesi gerçeğidir; Java ise, uygulamada, çoğu C ++ programının yığında nesneler tahsis etme eğiliminde olmasından dolayı, tahsisat maliyetinin genellikle sıfıra yakın olduğu anlamına gelir.

C ++ 'nın Java'nın gerisinde kaldığı bir alan, yığın üzerinde çok sayıda küçük nesnenin ayrılması gereken herhangi bir durumdur. Bu durumda, Java'nın çöp toplama sistemi muhtemelen standart newve deleteC ++ ' dan daha iyi bir performansa neden olacaktır , çünkü Java GC toplu olarak dağıtmayı mümkün kılar. Fakat yine de, bir C ++ programcısı bunu bir bellek havuzu veya döşeme ayırıcısı kullanarak telafi edebilir, oysa bir Java programcısı, Java çalışma zamanının optimize edilmediği bir bellek ayırma modeliyle karşılaştığında başvuru yapmaz.

Ayrıca, bu konu hakkında daha fazla bilgi için bu mükemmel cevaba bakınız .


6
İyi bir cevap ama küçük bir nokta: "C ++ şablonları ikinizi de verir (daha uzun derleme süreleri pahasına)." Ayrıca daha büyük program boyutunun pahasına ekleyeceğim. Her zaman sorun olmayabilir, ancak mobil cihazlar için geliştiriliyorsa kesinlikle olabilir.
Leo,

9
@luiscubal: hayır, bu bağlamda, C # genericleri çok Java benzeridir (aynı "jenerik" kod yolunun hangi tiplerden geçtiğine bakılmaksızın alındığı gibi). C ++ şablonlarının hilesi, kodun bir kez uygulanan her tür. Yani sadece ints için std::vector<int>tasarlanmış dinamik bir dizi ve derleyici buna göre optimize edebiliyor. AC # hala sadece bir . List<int>List
jalf,

12
@jalf C # List<int>kullanır int[], Object[]benzer bir Java kullanmaz. Bkz stackoverflow.com/questions/116988/...
luiscubal

5
@luiscubal: terminolojiniz belirsiz. JIT "derleme zamanı" olarak düşündüğüm gibi davranmıyor. Tabii ki, yeterince akıllı ve agresif bir JIT derleyicisi verildiğinde haklısın, yapabileceklerinin hiçbir sınırı yok. Ancak C ++ bu davranışı gerektirir . Dahası, C ++ şablonları programcının açık uzmanlıklar belirlemesini sağlar ve uygulanabilir olduğunda ilave açık iyileştirmeler sağlar. C # bunun için eşdeğeri yok. Örneğin, C ++ 'da, el kodlu SIMD uygulamamın vector<N>belirli bir örneği için nerede kullanılması gerektiğini tanımlayabilirimvector<4>
jalf

5
@ Leo: Şablonlar aracılığıyla kod şişirme 15 yıl önce bir sorun oldu. Yoğun sıcaklıklandırma ve iç içe yazma işlemlerinin yanı sıra, o zamandan beri toplanan yetenek derleyicileri (aynı örnekleri katlama gibi), günümüzde çok sayıda kod şablonlar aracılığıyla küçülüyor .
sbi

46

Diğer cevapların (şimdiye kadar 6'sı) bahsetmeyi unutmuş göründüğü gibi, ama bunu cevaplamak için çok önemli olduğunu düşündüğüm şey, Stroustrup tarafından 1. günden itibaren formüle edilen ve kullanılan C ++ 'temel tasarım felsefelerinden biri:

Kullanmadığın kadarını ödemiyorsun.

C ++ 'ı (özellikle belirli bir paradigmaya zorlanmamanız gerektiği gibi) büyük ölçüde şekillendiren diğer önemli tasarım ilkeleri vardır, ancak kullanmadığınız şeylerin parasını ödeyemezsiniz .


Stroustrup , C ++ 'nın Tasarım ve Evrimi adlı kitabında (genellikle [D&E] olarak adlandırılır) kitabında, ilk başta C ++ ile başa çıkması için neye ihtiyacı olduğunu açıklar. Kendi sözlerime göre: Doktora tezi için (ağ simülasyonları ile ilgili bir şey, IIRC), SIMULA'da çok sevdiği bir sistem kurdu, çünkü dil onun düşüncelerini doğrudan kodlamada ifade etmesinde çok iyiydi. Bununla birlikte, ortaya çıkan program çok yavaş yürüdü ve bir dereceye kadar almak için, C'nin öncülü olan BCPL'deki şeyi yeniden yazdı. BCPL'de kod yazarken bir acı olarak nitelendirdi, ancak ortaya çıkan program sunacak kadar hızlıydı. doktora çalışmasını sağlayan sonuçlar.

Ondan sonra, gerçek dünyadaki sorunları mümkün olduğunca doğrudan koda dönüştürecek, ancak aynı zamanda kodun çok verimli olmasına izin verecek bir dil istedi.
Bunu takiben, daha sonra C ++ olanı yarattı.


Dolayısıyla, yukarıda belirtilen hedef, sadece temel tasarım ilkesinden sadece biri değildir, C ++ 'a raisona çok yakındır . Ve hemen hemen her dilde bulunabilir: İşlevler yalnızca virtualistediğinizde (sanal işlevlerin çağrılması hafif bir ek yük ile geldiğinden) gelir. POD'lar yalnızca bunu açıkça istediğinizde otomatik olarak başlatılır, istisnalar yalnızca gerçekten Bunları fırlatın (yığın çerçevelerinin kurulum / temizliğinin çok ucuz olmasına izin vermek açık bir tasarım hedefiydi), böyle hissettiğinde GC çalışmaz.

C ++, performans karşılığında size bazı kolaylıklar ("bu yöntemi sanallaştırmak zorunda mıyım?") Vermemeyi açıkça seçti ("hayır, yapmam, ve şimdi derleyici bunu yapabilir inlineve heck’i optimize edebilir . Her şey! ") ve şaşırtıcı olmayan bir şekilde, bu gerçekten de daha uygun dillere kıyasla performans kazanımlarına neden oldu.


4
Kullanmadığın kadarını ödemiyorsun. => ve ardından RTTI :(
Matthieu M.

11
@Matthieu: Duygunuzu anladığım halde, yardım edemem ama bunun performansla ilgili dikkatle eklendiğini fark etmiyorum. RTTI, sanal tablolar kullanılarak uygulanabilecek şekilde belirlenmiştir ve bu nedenle kullanmazsanız çok az ek yük ekler. Polimorfizm kullanmazsanız, hiçbir maliyeti yoktur. Bir şey mi eksik?
sbi

9
@ Matthieu: Tabii ki, sebep var. Fakat bu sebep rasyonel mi? Görebildiğim kadarıyla, kullanılmadığı takdirde, "RTTI maliyeti", her polimorfik sınıfın sanal tablosunda, statik olarak bir yere tahsis edilen bazı RTTI nesnelerine işaret eden ek bir göstericidir. Çipi kızartma makinemde programlamak istemiyorsanız, bu nasıl alakalı olabilir?
sbi

4
@Aaronaught: Buna ne cevap vereceğim konusunda kaybediyorum. Yanıtımı gerçekten reddettiniz, çünkü Stroustrup ve arkadaşlarının bu yolları ve özellikleri ayrı ayrı listelemek yerine performansa olanak sağlayacak bir biçimde özellikler ekleyen temel felsefeye dikkat çekiyor mu?
sbi

9
@Aaronaught: Sende benim sempatim var.
sbi

29

Bu konuyla ilgili Google araştırma makalesini biliyor musunuz ?

Sonuç olarak:

Performans açısından C ++ 'ın büyük bir farkla kazandığını tespit ettik. Bununla birlikte, çoğu ortalama programcıya sunulmayacak düzeyde bir karmaşıklık düzeyinde yapılmış olan en kapsamlı ayarlama çabalarını gerekli kılmıştır.

Bu, en azından kısmen, "gerçek dünya C ++ derleyicileri, ampirik önlemlerle Java derleyicilerinden daha hızlı kod ürettiği için" anlamında bir açıklamadır.


4
Bellek ve önbellek kullanım farklılıklarının yanı sıra, en önemlilerinden biri gerçekleştirilen optimizasyon miktarıdır. GCC / LLVM'in (ve muhtemelen Visual C ++ / ICC'nin) Java HotSpot derleyicisine göre ne kadar optimizasyon yaptığını karşılaştırın: özellikle döngülerle ilgili olarak, fazlalık dalları ortadan kaldırmak ve kayıt tahsisini çok daha fazla. JIT derleyicileri genellikle bu agresif optimizasyonlar için zamana sahip değildir, hatta mevcut çalışma zamanı bilgisini kullanarak daha iyi uygulayabileceklerini düşünmüştür.
Gratian Lup

2
@GratianLup: Bunun LTO için doğru (hala) olup olmadığını merak ediyorum.
Deduplicator

2
@GratianLup: C ++ için profil destekli optimizasyonları unutmayalım ...
Deduplicator

23

Bu, sorularınızın bir kopyası değildir, ancak kabul edilen cevap, sorunuzun çoğuna cevap verir: Java'nın modern bir incelemesi

Sonuç olarak:

Temel olarak, Java anlambilimi, C ++ 'dan daha yavaş bir dil olduğunu belirtir.

Bu nedenle, hangi dilden C ++ 'ı karşılaştırdığına bağlı olarak, aynı cevabı alabilirsiniz veya almayın.

C ++ 'ta sahip olduğunuz:

  • Akıllı astar yapma kapasitesi,
  • güçlü bir yere sahip genel kod üretimi (şablonlar)
  • mümkün olduğu kadar küçük ve kompakt veri
  • indirmeleri önlemek için fırsatlar
  • öngörülebilir hafıza davranışı
  • derleyici optimizasyonları yalnızca üst düzey soyutlamalar (şablonlar) kullanımı nedeniyle mümkündür

Bunlar, dil tanımının hafızada ve hızda teorik olarak daha verimli olmasını sağlayan herhangi bir dilden daha güçlü olmasını sağlayan özellikleri veya yan etkileridir:

  • Kitlesel kullanımı ("her şey yönetilen bir referans / işaretçidir" dilleri) C ++ olarak küçük veriye sahip olsa bile lot;
  • üyelere dolaylı olarak erişilen büyük boyutlu nesneler üretir: bu, varsayılan olarak referanslara sahip olmanın bir sonucudur, üyeler işaretçilerdir, böylece bir üye aldığınızda, ana nesnenin çekirdeğine yakın veri alamazsınız, tekrar önbellek özeti tetiklenir.
  • Bir çöp toplayıcı kullanın: Sadece performansın öngörülebilirliğini imkansız kılar (tasarım gereği).

C ++ 'nın derleyicinin saldırgan dizilişi bir çok yüklemeyi azaltır veya yok eder. Küçük bir kompakt veri kümesi oluşturma kapasitesi, bu verileri bir araya getirmek yerine hafızanın tamamına yaymazsanız önbellek dostu yapar (her ikisi de mümkündür, C ++ seçmenize izin verir). RAII, C ++ bellek davranışını tahmin edilebilir kılar ve gerçek zamanlı ya da yarı gerçek zamanlı simülasyonlarda yüksek hız gerektiren birçok problemi ortadan kaldırır. Yerellik sorunları, genel olarak şu şekilde özetlenebilir: program / veriler ne kadar küçükse, yürütme o kadar hızlı olur. C ++, verilerinizin olmasını istediğiniz yerde (bir havuzda, bir dizide veya her neyse) ve kompakt olduğundan emin olmak için çeşitli yollar sunar.

Açıkçası, aynı şeyi yapabilen başka diller var, ancak C ++ kadar çok soyutlama araçları sağlamadıkları için daha az popülerler, bu yüzden birçok durumda daha az kullanışlı oluyorlar.


7

Esas olarak (Michael Borgwardt'ın dediği gibi) içine biraz JIT verimsizliği eklenmiş bellekle ilgilidir.

Söz edilmeyen bir şey, önbellektir - önbelleği tamamen kullanmak için, sürekli olarak (yani hep birlikte) ortaya konması için verilerinize ihtiyacınız vardır. Şimdi bir GC sistemi ile, hızlı olan GC yığınına bellek tahsis edilir, ancak bellek kullanıldığında GC düzenli olarak devreye girer ve artık kullanılmayan blokları kaldırır ve ardından kalanı sıkıştırır. Şimdi bu kullanılmış blokları bir arada hareket ettirmenin bariz yavaşlığından ayrı olarak, bu, kullandığınız verilerin birbirine yapışmayabileceği anlamına gelir. 1000 öğeden oluşan bir diziniz varsa, hepsini bir kerede tahsis etmediğiniz sürece (ve sonra yığının sonunda oluşturulacak yeni öğeleri silmek ve oluşturmak yerine içeriklerini güncelleyebilirsiniz) bunlar yığının her tarafına dağılmış olur. bu nedenle, hepsini bellek önbelleğine okumak için birkaç bellek isabetine ihtiyaç duymak. AC / C ++ uygulaması büyük olasılıkla bu öğeler için bellek tahsis eder ve daha sonra blokları verilerle güncellersiniz. (Tamam, GC bellek ayırmalarına daha çok benzeyen bir liste gibi veri yapıları var, ancak insanlar bunların vektörlerden daha yavaş olduğunu biliyorlar).

Herhangi bir StringBuilder nesnesini String ile değiştirerek bu işlemi görebilirsiniz ... Stringbuilders, belleği önceden tahsis ederek ve doldurarak çalışır ve java / .NET sistemleri için bilinen bir performans numarasıdır.

'Eski sil ve yeni kopyaları ayır' paradigmasının Java / C # 'da çok yoğun bir şekilde kullanıldığını unutmayın, çünkü insanlara GC nedeniyle bellek ayırma işlemlerinin gerçekten hızlı olduğu söylenir ve bu nedenle dağınık bellek modeli her yerde kullanılır ( stringbuilders hariç, elbette) böylece tüm kütüphaneleriniz hafızayı boşa harcar ve çoğunu kullanır, hiçbiri bitişiklikten faydalanamaz. Bunun için GC etrafındaki yutturmaca suçu - size hafızanın boş olduğunu söylediler, lol.

GC'nin kendisi açıkça bir başka mükemmel vuruştur - koşarken, sadece yığının içinden süpürmekle kalmaz, aynı zamanda kullanılmayan tüm blokları serbest bırakmak zorunda kalır ve daha sonra herhangi bir sonlandırıcıyı çalıştırması gerekir (bunun ayrı ayrı yapılması gerekir. Uygulamanın bir sonraki turu durdu) (hala böyle mükemmel bir vuruş olup olmadığını bilmiyorum, ama okuduğum tüm dokümanlar gerçekten gerekliyse sadece finalizatör kullandığını söylüyor) ve sonra bu blokları bulunduğu yere taşımak zorunda kaldı. sıkıştırılmış ve başvuruyu bloğun yeni konumuna güncelle. Gördüğünüz gibi, bu çok iş!

C ++ hafızası için mükemmel isabetler, bellek tahsislerine gelir - yeni bir bloğa ihtiyaç duyduğunuzda, bir sonraki boş alanı aramak için yığından yürümek zorundasınız, ağır parçalanmış bir yığınla, bu neredeyse GC'ler kadar hızlı değildir. 'sadece sonda başka bir blok tahsis et' ancak GC GC sıkıştırma işleminin yaptığı kadar yavaş olmadığını ve birden fazla sabit boyutlu blok yığınları (aksi takdirde bellek havuzları olarak da bilinir) kullanarak hafifletilebileceğini düşünüyorum.

Güvenlik kontrolü, sonda yolu ( sxstrace'i aç ve sadece neyin işe yaradığına bak!) Ve java / .net ile çok daha popüler görünen genel diğer mühendislik gerektiren GAC’dan yükleme meclisleri gibi. C / C ++ 'dan daha fazla.


2
Yazdığınız birçok şey modern nesil çöp toplayıcıları için doğru değildir.
Michael Borgwardt

3
@MichaelBorgwardt gibi? "GC düzenli çalışıyor" ve "yığını sıkıştırıyor" diyorum. Cevabımın geri kalanı, uygulama veri yapılarının belleği nasıl kullandığı ile ilgilidir.
gbjbaanb

6

"Basitçe C ++, montaj / makine kodunda derlendiğinden, Java / C # hala çalışma zamanında JIT derlemesinin işlem yüküne sahip mi?" Temel olarak evet!

Yine de hızlı not, Java sadece JIT derlemesinden daha fazla ek yüke sahiptir. Örneğin, daha geniştir (gibi şeyler nasıl yapar ki sizin için kontrol etmez ArrayIndexOutOfBoundsExceptionsve NullPointerExceptions). Çöp toplayıcı, bir başka önemli yüküdür.

Burada oldukça ayrıntılı bir karşılaştırma var .


2

Aşağıdakilerin yalnızca yerel ve JIT derlemesi arasındaki farkı karşılaştırdığını ve herhangi bir dil veya çerçevenin özelliklerini kapsamadığını unutmayın. Bunun ötesinde belirli bir platform seçmek için meşru sebepler olabilir.

Biz yerel kod hızlıdır iddia, biz bahsediyoruz tipik kullanım durumunda hayır, mesela (anında sonuçlarla, bir JIT derlenmiş uygulamanın tipik kullanımı kullanıcı tarafından çalıştırılacak olan JIT derlenmiş kod, karşı doğal derlenmiş kod önce derleyici bekliyor). Bu durumda, kimsenin düz bir yüzle hak iddia edebileceğini sanmıyorum, JIT derlenmiş kodunun yerel kodla eşleşebileceğini veya yenebileceğini sanmıyorum.

Diyelim ki X dilinde yazılmış bir programımız olduğunu varsayalım ve onu yerel bir derleyici ile ve bir JIT derleyici ile derleyelim. Her iş akışı, aynı şekilde (Aşama -> Ara Temsil -> Makine Kodu -> Yürütme) genelleştirilebilecek aşamalara sahiptir. İkisi arasındaki en büyük fark, hangi aşamaların kullanıcı tarafından görüldüğü ve programcının gördüğü şeydir. Yerel derlemede, programcı yürütme aşamasından başka her şeyi görür, ancak JIT çözümünde yürütme işlemine ek olarak makine koduna derleme kullanıcı tarafından görülür.

A'nın B'den daha hızlı olduğu iddiası , programın çalışması için kullanıcı tarafından görüldüğü gibi geçen süreyi ifade eder . Her iki kod parçasının da Yürütme aşamasında aynı şekilde performans gösterdiğini varsayarsak, JIT iş akışının, kullanıcı için daha yavaş olduğunu varsaymalıyız, çünkü derlemenin T zamanını T> 0 olan makine koduna T zamanını görmesi gerekir. JIT iş akışının, kullanıcının yerel iş akışıyla aynı şekilde gerçekleştirilmesi olasılığına karşı, kullanıcıya, Yürütme + Derlemesi ile makine kodunun yürütülmesi süresini, Yürütme aşamasından daha düşük olacak şekilde azaltmalıyız. yerel iş akışının. Bu, JIT derlemesinde kodu yerel derlemeden daha iyi duruma getirmemiz gerektiği anlamına gelir.

Bununla birlikte, bu oldukça zordur, çünkü Yürütmeyi hızlandırmak için gerekli optimizasyonları gerçekleştirmek için, makine kodu aşamasında derlemeye daha fazla zaman harcamalıyız ve bu nedenle, optimize edilmiş kodun bir sonucu olarak kaydettiğimiz her zaman gerçekten kaybedilir. derlemeye ekleriz. Başka bir deyişle, JIT tabanlı bir çözümün "yavaşlığı" yalnızca JIT derlemesi için fazladan zaman harcadığından değil, bu derleme tarafından üretilen kod yerel bir çözümden daha yavaş çalışır.

Bir örnek kullanacağım: Kayıt tahsisi. Bellek erişimi, kayıt erişiminden binlerce kez daha yavaş olduğu için, mümkün olan her yerde kayıtları kullanmak istiyoruz ve mümkün olduğunca az bellek erişimine sahip olmak istiyoruz, ancak sınırlı sayıda kayıt sahibiz ve gerektiğinde belleği belleğe dökmeliyiz. Bir kayıt. Hesaplamak için 200ms alan bir kayıt tahsisat algoritması kullanırsak ve sonuç olarak 2ms yürütme süresinden tasarruf edersek - bir JIT derleyicisi için en iyi zamanı kullanmıyoruz. Yüksek derecede optimize edilmiş kod üretebilen Chaitin algoritması gibi çözümler uygun değildir.

JIT derleyicisinin rolü, derleme zamanı ile üretilen kodun kalitesi arasındaki en iyi dengeyi sağlamaktır, ancak kullanıcıyı derhal bekletmek istemediğiniz için hızlı derleme zamanı konusunda büyük önyargılara sahiptir. Yerel derleyici kodu en iyi duruma getirmede zaman zaman bağlı olmadığından, en iyi algoritmaları kullanmakta serbest olduğundan, JIT durumunda yürütülen kodun performansı daha yavaştır. Bir JIT derleyicisinin genel derleme + yürütmesinin yalnızca yerel olarak derlenen kod için yürütme süresini yenebilmesi olasılığı etkili bir şekilde 0'dır.

Ancak, VM'lerimiz yalnızca JIT derlemesiyle sınırlı değildir. Önceden derleme derleme teknikleri, önbellekleme, çalışırken değiştirme ve uyarlanabilir optimizasyonlar kullanıyorlar. Öyleyse performansın, kullanıcının gördüğü olduğu şeklini değiştirelim ve programı yürütmek için harcanan zamanı sınırlayalım (derlediğimizi varsayalım). Yürütme kodunu yerel derleyiciye eşdeğer (veya belki de daha iyi?) Etkin bir şekilde yapabiliriz. VM'ler için büyük bir iddia, yerel bir derleyiciden sonra daha iyi kalitede kod üretebilecekleri olabilir, çünkü daha fazla bilgiye erişim sağlayabiliyor - belirli bir işlevin ne sıklıkta yürütülebileceği gibi çalışan sürecinki. Sanal makine daha sonra çalışırken değiştirilebilir en temel koda adaptif optimizasyonlar uygulayabilir.

Yine de bu argümanla ilgili bir sorun var - bu, profil güdümlü optimizasyonun ve benzerlerinin VM'ler için benzersiz bir şey olduğunu varsayar, bu doğru değildir. Uygulamamızı profil etkinleştirilmiş olarak derleyerek, bilgileri kaydederek ve ardından uygulamayı bu profille yeniden derleyerek bunu yerel derlemeye de uygulayabiliriz. Kod değiştirme işleminin yalnızca bir JIT derleyicisinin yapabileceği bir şey olmadığını da belirtmek önemlidir, bunun için yerel kod için yapabiliriz - bunu yapmak için JIT tabanlı çözümler daha kolay erişilebilir ve geliştirici için çok daha kolay. Öyleyse asıl soru şudur: Bir VM bize yerel derlemenin yapamayacağı, kodumuzun performansını artırabilecek bir bilgi verebilir mi?

Kendim göremiyorum. İşlem daha fazla dahil olsa da, tipik bir VM tekniklerinin çoğunu da yerel koda uygulayabiliriz. Benzer şekilde, yerel bir derleyicinin tüm optimizasyonlarını AOT derlemesi veya uyarlamalı optimizasyonlar kullanan bir VM'ye geri uygulayabiliriz. Gerçek şu ki, yerel olarak çalışan kod ile VM'de çalışanlar arasındaki fark, inandığımız kadar büyük değil. Nihayetinde aynı sonuca götürürler, ancak oraya ulaşmak için farklı bir yaklaşım izlerler. VM, yerel derleyicinin başından itibaren beklediği (ve yinelemeli bir yaklaşımla geliştirilebilir) optimize edilmiş kod üretmek için yinelemeli bir yaklaşım kullanır.

Bir C ++ programcısı baştan beri optimizasyonlara ihtiyacı olduğunu savunabilir ve bir VM'nin nasıl yapılacağına karar vermesini beklememelidir. VM'lerimizdeki mevcut optimizasyon seviyesi, yerel derleyicilerin sunabileceğinden daha düşük olduğu için bu muhtemelen muhtemelen geçerli teknolojimizde geçerli bir nokta - ancak VM'lerimizdeki AOT çözümleri geliştiği zaman her zaman böyle olmayabilir.


0

Bu makale , c ++ vs c # 'nin hızını ve yüksek performanslı kod almak için her iki dilde de üstesinden gelmeniz gereken sorunları karşılaştırmaya çalışan bir dizi blog yazısı özetidir. Özet, 'kütüphaneniz her şeyden çok daha önemlidir, ancak c ++' taysanız bunun üstesinden gelebilirsiniz. ' ya da 'modern dillerin kütüphaneleri daha iyidir ve bu nedenle felsefenizin eğimine bağlı olarak daha az çabayla daha hızlı sonuçlar elde edersiniz'.


0

Buradaki asıl sorunun "hangisi daha hızlı?" Olmadığını düşünüyorum. ancak "hangisi daha yüksek performans için en iyi potansiyele sahip?". Bu şartlara bakıldığında, C ++ açıkça kazanıyor - yerel kodlara göre derlendi, JITT yok, daha düşük bir soyutlama seviyesi var.

Bu tam hikayeden uzak.

C ++ derlenmiş olduğundan, derleme zamanında herhangi bir derleyici optimizasyonu yapılmalıdır ve bir makine için uygun derleyici optimizasyonları diğeri için tamamen yanlış olabilir. Aynı zamanda, herhangi bir global derleyici optimizasyonunun bazı algoritmaları veya kod kalıplarını diğerlerine göre tercih edebileceği ve tercih edeceği durumdur.

Öte yandan, bir JITted programı JIT zamanında optimizasyon yapar, bu nedenle önceden derlenmiş bir programın çalıştığı makine ve üzerinde çalıştığı kod için çok özel optimizasyonlar yapamayacağı ve çok özel optimizasyonlar yapabileceği bazı püf noktaları alabilir. JIT’in ilk ek yükünü geçtiğinizde, bazı durumlarda daha hızlı olma potansiyeli vardır.

Her iki durumda da algoritmanın mantıklı bir şekilde uygulanması ve aptal olmayan programcının diğer örnekleri muhtemelen çok daha önemli faktörler olacaktır, ancak - örneğin, C ++ 'a bile tam olarak sarılacak olan tamamen beyinsiz dize kodlarını yazmak mümkündür. yorumlanmış bir betik dili.


3
"Bir makine için uygun olan derleyici optimizasyonları bir başkası için tamamen yanlış olabilir" Eh, bu gerçekten dili suçlamıyor. Gerçekten de performans açısından kritik olan kod, çalışacağı her makine için ayrı olarak derlenebilir; bu, kaynaktan ( -march=native) yerel olarak derleme yaparsanız, beyin fırtınası gerektirmez . - "daha düşük bir soyutlama seviyesi" gerçekten doğru değil. C ++, tıpkı Java kadar yüksek seviyeli soyutlamalar kullanır (ya da aslında daha yüksekler: işlevsel programlama? Şablon metaprogramı?), Soyutlamaları sadece Java'dan daha "temiz" uygular.
leftaroundabout

"Gerçekten performans açısından kritik olan kod, çalıştırılacağı her makine için ayrı olarak derlenebilir, bu, kaynaktan yerel olarak derlerseniz, hiç akıllıca olmaz" - bu, son kullanıcının da bir programcı olduğu varsayımından dolayı başarısız olur.
Maximus Minimus

Mutlaka son kullanıcı değil, sadece programı kurmaktan sorumlu kişi. Masaüstü ve mobil cihazlarda, bu tipik bir son kullanıcı, ama bunlar, orada kesinlikle en performansa kritik olanları sadece uygulamalar değildir. Ve eğer tüm iyi özgür / açık yazılım projeleri gibi düzgün bir şekilde derlenmiş komut dosyaları yazdıysa, kaynaktan bir program oluşturmak için bir programcı olmanıza gerek yok.
leftaroundabout

1
Teoride evet olsa da, bir JIT pratikte statik bir derleyiciden daha fazla numara çekebilirken, pratikte (en azından. Son zamanlarda .NET JIT kodunun bir demetini söküp yaptım ve .NET JIT’in yapmaması gereken kodlardan ilmekleri kaldırmak, kodları silmek gibi her türlü optimizasyon var. Keşke isterdim, ama hey, microsoft içindeki windows takımı yıllardır .NET'i öldürmeye çalışıyor, bu yüzden nefesimi tutmuyorum
Orion Edwards

-1

JIT derlemesi aslında performans üzerinde olumsuz bir etkiye sahiptir. "Mükemmel" bir derleyici ve "mükemmel" bir JIT derleyici tasarlarsanız, ilk seçenek her zaman performansta kazanır.

Hem Java hem de C # ara dillere yorumlanır ve ardından çalışma zamanında yerel koda derlenir, bu da performansı düşürür.

Ancak şimdi fark C # için belirgin değil: Microsoft CLR farklı CPU'lar için farklı yerel kodlar üretiyor, bu nedenle kod her zaman C ++ derleyicileri tarafından yapılmayan makine için daha verimli çalışıyor.

PS C # çok etkili bir şekilde yazılmıştır ve çok fazla soyutlama katmanı yoktur. Bu Java için geçerli değil, o kadar verimli değil. Bu nedenle, bu durumda, CLR büyüklüğü ile C # programları genellikle C ++ programlarından daha iyi performans gösterir. Net ve CLR hakkında daha fazla bilgi için Jeffrey Richter'ın "CLR via C #" kısmına bakınız .


8
Eğer JIT gerçekten performans üzerinde olumsuz bir etkiye sahip olsaydı, kesinlikle kullanılmayacaktı?
Zavior

2
@Zavior - Sorunuza iyi bir cevap düşünemiyorum, ancak JIT’in ek yükü nasıl ekleyemediğini göremiyorum - JIT, ortaya çıkan kaynakları gerektiren çalışma zamanında tamamlanması gereken ekstra bir işlemdir. Programın yürütülmesi için harcanması, tamamıyla derlenmiş bir dil 'kullanıma hazır'.
Anonim

3
JIT'in performans üzerinde olumlu bir etkisi vardır, olumsuzu değil, bağlama koyarsanız - Bayt kodunu çalıştırmadan önce makine koduna derlemek . Sonuçlar, yorumlanan eşdeğer bayt kodundan daha hızlı çalışmasına izin vererek önbelleğe alınabilir.
Casey Kuball

3
JIT (veya bytecode yaklaşımı) performans için değil, kullanım kolaylığı için kullanılır. Her platform için (veya her biri için en uygun olan ortak bir alt küme) ön-binicilerden önce, sadece yarı yolda derleyin ve JIT derleyicisinin gerisini halletmesine izin verin. 'Bir kere yaz, herhangi bir yere dağıt' bu yüzden böyle yapılıyor. Kolaylık sadece baytkodu tercüman ile bulunabilmektedir, ancak önceden derlenmiş bir çözüm yenmek için mutlaka yeterince hızlı olsa JIT (ham tercüman daha hızlı yapmak gelmez; JIT derleme yapar zaman alır ve sonucu her zaman yapmaz onun için).
tdammers

4
@Tdammmers, aslında bir performans bileşeni de var. Bkz java.sun.com/products/hotspot/whitepaper.html . Optimizasyonlar, dal tahminini ve önbellek isabetlerini iyileştirmek için dinamik ayarlamalar, dinamik satır içi, sanallaştırma dışı bırakma, sınır kontrolünü devre dışı bırakma ve döngü açma işlemlerini içerebilir. İddia, çoğu durumda bunların JİT’in bedelini ödemekten daha fazla olabileceğidir.
Charles E. Grant
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.