scala vs java, performans ve bellek? [kapalı]


160

Scala'ya bakmaya hevesliyim ve cevap bulamadığım tek bir temel sorum var: Genel olarak, Scala ve Java arasında bellek performansı ve kullanımında bir fark var mı?


3
Performansın çok yakın olabileceğini iddia ettim. Ne yaptığınıza çok bağlı olduğundan şüpheleniyorum. (Java vs C için olduğu gibi)
Peter Lawrey

Bu tür soruların cevabı "duruma bağlıdır" - sistem X ile sistem Y'nin hemen hemen her karşılaştırması için. Bu, stackoverflow.com/questions/2479819/…
James Moore

Yanıtlar:


261

Scala, farkında olmadan muazzam miktarda belleği kullanmayı çok kolaylaştırır. Bu genellikle çok güçlüdür, ancak bazen sinir bozucu olabilir. Örneğin, bir dizi dizeye (denir array) ve bu dizelerden dosyalara (denir mapping) bir haritanız olduğunu varsayalım . Haritada bulunan ve ikiden büyük uzunluktaki dizelerden gelen tüm dosyaları almak istediğinizi varsayalım. Java'da,

int n = 0;
for (String s: array) {
  if (s.length > 2 && mapping.containsKey(s)) n++;
}
String[] bigEnough = new String[n];
n = 0;
for (String s: array) {
  if (s.length <= 2) continue;
  bigEnough[n++] = map.get(s);
}

Whew! Zor iş. Scala'da aynı şeyi yapmanın en kompakt yolu:

val bigEnough = array.filter(_.length > 2).flatMap(mapping.get)

Kolay! Ancak, koleksiyonların nasıl çalıştığına oldukça aşina değilseniz, fark etmeyebileceğiniz şey, bunu yapmanın bu yolunun, dizinin her öğesi (ile , döndüren filter) için fazladan bir ara dizi (ile ) ve ekstra bir nesne oluşturmasıdır. bir seçenek). Ayrıca, işlev nesneleri küçük olduğu için nadiren büyük bir sorun olsa da, iki işlev nesnesi (biri filtre ve biri flatMap için) oluşturur.mapping.get

Yani, temel olarak, bellek kullanımı, ilkel düzeyde aynıdır. Ancak Scala'nın kütüphaneleri, çok sayıda (genellikle kısa ömürlü) nesneleri kolayca oluşturmanızı sağlayan birçok güçlü yönteme sahiptir. Çöp toplayıcı genellikle bu tür çöplerle oldukça iyidir, ancak hangi belleğin kullanıldığından tamamen habersiz kalırsanız, Scala'da muhtemelen Java'dan daha erken sorun yaşarsınız.

Bilgisayar Dilleri Karşılaştırma Oyunu Scala kodunun Java benzeri bir performans elde etmek için oldukça Java benzeri bir tarzda yazıldığını ve dolayısıyla Java benzeri bellek kullanımına sahip olduğunu unutmayın. Bunu Scala'da yapabilirsiniz: kodunuzu yüksek performanslı Java koduna benzeyecek şekilde yazarsanız, yüksek performanslı Scala kodu olacaktır. (Sen olabilir daha deyimsel Scala tarzında yazmak ve hala iyi bir performans elde edebilmek, ancak özelliklerine bağlıdır.)

Programlama için harcanan zaman başına, Scala kodumun genellikle Java kodumdan daha hızlı olduğunu eklemeliyim çünkü Scala'da performans açısından kritik olmayan sıkıcı parçaları daha az çaba ile yapabilirim ve dikkatimi daha fazla algoritmaları optimize ederek harcayabilirim ve performans açısından kritik parçalar için kod.


172
Bu son paragraf için +1. Bu dikkate dışında kalmasın hayati nokta kadar çok sık.
Kevin Wright

2
Görüşlerin bahsettiğiniz sorunlara çok yardımcı olabileceğini düşündüm . Yoksa özellikle diziler için doğru değil mi?
Nicolas Payette

1
@Kevin Wright - "Bu çok sık göz önünde bulundurulmayan hayati bir nokta" - Bu söylemesi kolay ve göstermesi zor bir şey ve bize Rex Kerr'in becerileri hakkında daha az yetenekli olanların elde edemediği şeyler hakkında bir şeyler söyleyin.
igouy

1
>> üstün performans ile deyimsel tarzda << Kıyaslama Scala programları için "üstün" performans elde etmeyen kıyaslama oyununda yer var.
igouy

2
@RexKerr - Java örneğiniz, Scala örneğinizin Dizeler seçildikten sonra yalnızca bir kez yaptığı her olası Dize için eşleme anahtarını iki kez aramıyor mu? Yani farklı veri kümeleri için farklı şekillerde optimize edilmişler mi?
Seth

103

Ben yeni bir kullanıcıyım, bu yüzden yukarıdaki Rex Kerr'ın cevabına bir yorum ekleyemiyorum (yeni kullanıcıların "cevap vermesine izin veriyor, ancak" yorum "çok garip bir kural btw).

Rex'in yukarıdaki popüler cevabının "vay canına, Java çok ayrıntılı ve bu kadar zor bir iş" imalarına yanıt vermek için kaydoldum. Elbette daha kısa Scala kodu yazabilirsiniz, ancak verilen Java örneği açıkça şişirilmiştir. Çoğu Java geliştiricisi böyle bir şeyi kodlar:

List<String> bigEnough = new ArrayList<String>();
for(String s : array) {
  if(s.length() > 2 && mapping.get(s) != null) {
    bigEnough.add(mapping.get(s));
  }
}

Ve elbette, Eclipse'in gerçek yazmanın çoğunu sizin için yapmadığını ve kaydedilen her karakterin sizi gerçekten daha iyi bir programcı yaptığını varsayarsak, bunu kodlayabilirsiniz:

List b=new ArrayList();
for(String s:array)
  if(s.length()>2 && mapping.get(s) != null) b.add(mapping.get(s));

Şimdi sadece tam değişken isimler ve kıvırcık parantezler yazmak için harcadığım zamanı kurtarmakla kalmadım (derin algoritmik düşünceleri düşünmek için 5 saniye daha harcamamı sağladım), aynı zamanda kodumu gizleme yarışmalarına da girebilir ve potansiyel olarak ekstra para kazanabilirim. Bayram.


7
Neden "ayın kalça dili" kulübüne üye değilsin? Güzel yorumlar. Özellikle son paragrafı okumaktan zevk aldım.
stepanian

21
Mükemmel koy! Şişirilmiş Java kodunun dikkatlice oluşturulmuş, Scala'nın (veya başka bir FP dilinin) bazı örneklerini ve ardından Scala'nın Java'dan daha iyi olması gerektiğine dair aceleyle çizilmiş bir sonuca ulaştığı, onaylanmış örneklerden bıktım. Kim zaten Scala'da önemli bir şey yazdı ki! ;-) Ve Twitter
demeyin

2
Rex'in çözümü, dizinin hafızasını önceden düzenler, bu da derlenmiş kodun daha hızlı çalışmasını sağlar (çünkü yaklaşımınızla JVM'nin dizinizi büyüdükçe periyodik olarak yeniden tahsis etmesine izin verirsiniz). Daha fazla yazım olmasına rağmen, performans açısından bir kazanan olabilir.
Ashalynd

5
biz iken, java8 olacak:Arrays.stream(array).map(mapping::get).filter(x->x!=null).toArray(File[]::new);
bennyl

2
Scala'yı bazı yönlerden Java'dan "daha iyi" yapan şey, türler (Monads, Functors, vb.) Gibi daha genel kalıpları ifade etmeyi kolaylaştıran genişletilmiş tür sistem yetenekleridir. Bu, Java'da sık sık olduğu gibi aşırı sıkı sözleşmeler nedeniyle yolunuza çıkmayan türler oluşturmanıza olanak tanır. Koddaki gerçek kalıplara dayanmayan katı sözleşmeler, yalnızca kodunuzu düzgün bir şekilde birim test etmek için Sorumluluk kalıplarının tersine çevrilmesinin gerekli olmasının nedenidir (Bağımlılık Enjeksiyonu önce akla gelir ve getirdiği XML Cehennemi). Addl. esnekliğin getirdiği kısaca sadece bir bonus.
josiah

67

Scala'nızı Java gibi yazın ve neredeyse aynı metriklerle neredeyse aynı bayt kodunun yayınlanmasını bekleyebilirsiniz.

Değişmez nesneler ve daha üst düzey işlevlerle daha "deyimsel olarak" yazın ve biraz daha yavaş ve biraz daha büyük olacaktır. Bu genel kuralın tek istisnası, tür parametrelerinin @specialisedek açıklamayı kullandığı genel nesneleri kullanırken , bu, boks / kutudan kaçınarak Java'nın performansını geride bırakabilecek daha büyük bayt kodu oluşturacaktır.

Ayrıca, paralel olarak çalıştırılabilen kod yazarken daha fazla bellek / daha az hızın kaçınılmaz bir değiş tokuş olduğu gerçeğidir. Deyimsel Scala kodu doğada tipik Java kodundan çok daha bildirgendir ve genellikle .partam paralel olmaktan sadece 4 karakter ( ) uzaktadır.

Yani eğer

  • Scala kodu tek bir iş parçacığında Java kodundan 1.25x daha uzun sürer
  • Bu edilebilir kolayca 4 çekirdek (dizüstü bilgisayarlar artık yaygın çift) halinde bölünmüş
  • orijinal Java'nın (1.24 / 4 =) 0.3125x paralel çalışma süresi için

O zaman Scala kodunun şimdi nispeten% 25 daha yavaş veya 3 kat daha hızlı olduğunu söyleyebilir misiniz?

Doğru cevap, tam olarak "performansı" nasıl tanımladığınıza bağlıdır :)


4
Bu arada, bunun .par2.9'da olduğunu belirtmek isteyebilirsiniz .
Rex Kerr

26
>> Scala kodunun şimdi% 25 daha yavaş veya 3 kat daha hızlı olduğunu söyleyebilir misiniz? << Çok iş parçacıklı Java koduyla varsayımsal karşılaştırmanızın neden olmadığını söyleyebilirim?
igouy

17
@igouy - Mesele şu ki, sözde varsayımsal kod mevcut değil, "daha hızlı" Java kodunun zorunlu doğası paralellik yapmayı çok daha zorlaştırıyor, öyle ki maliyet / fayda oranı hiç olma ihtimalinin olmadığı anlamına geliyor. Öte yandan, deyimsel Scala, doğada çok daha açıklayıcı olmak, önemsiz bir değişiklikten başka bir şeyle eşzamanlı hale getirilebilir.
Kevin Wright

7
Eşzamanlı Java programlarının varlığı, tipik bir Java programının eşzamanlılığa kolayca uyarlanabileceği anlamına gelmez . Bir şey varsa, belirli çatal birleştirme stilinin Java'da özellikle nadir olduğunu ve açıkça kodlanması gerektiğini söyleyebilirim, oysa minimum içerilen değeri bulma veya bir koleksiyondaki değerlerin toplamı önemsiz bir şekilde paralel olarak yapılabilir Scala'da sadece kullanarak .par.
Kevin Wright

5
Hayır. Bu tür bir şey birçok algoritma için temel bir yapı taşıdır ve dil ve standart kütüphanelerde (sadece tipik olanların değil, tüm programların kullanacağı aynı standart kütüphaneler) bu kadar düşük bir seviyede mevcut olduğunu görmek, ' sadece dili seçerek eşzamanlı olmaya daha yakınsınız. Örneğin, bir koleksiyonun üzerindeki eşleme, paralel olarak paralelleştirmeye uygundur ve mapyöntemi kullanmayan Scala programlarının sayısı kaybolarak az olacaktır.
Kevin Wright

31

Bilgisayar Dil Deneyleri Oyunu:

Hız testi java / scala 1.71 / 2.25

Bellek testi java / scala 66.55 / 80.81

Yani, bu kriterler java'nın% 24 daha hızlı olduğunu ve scala'nın% 21 daha fazla bellek kullandığını söylüyor.

Sonuçta bu büyük bir sorun değil ve çoğu zaman veritabanı ve ağ tarafından tüketildiği gerçek dünya uygulamalarında önemli olmamalıdır.

Alt satır: Scala sizi ve ekibinizi (ve ayrıldığınızda projeyi devralan insanları) daha üretken yaparsa, o zaman bunun için gitmelisiniz.


34
Kod boyutu Java / Scala 3.39 / 2.21
Hamar

22
Bu gibi sayılara dikkat edin, gerçekte neredeyse hiçbir şey ifade etmiyorlarsa çok hassas görünüyorlar. Scala her zaman ortalama olarak Java'dan% 24 daha hızlı değil, vb. Değil
Jesper

3
Afaik tarafından verilen sayılar bunun tersini gösterir: Java, scala'dan% 24 daha hızlıdır. Ama dediğin gibi - bunlar gerçek uygulamalarda olanlarla eşleşmesi gerekmeyen mikrobençeler. Ve farklı dillerdeki farklı yol ya da problem çözümü sonunda daha az karşılaştırılabilir programlara yol açabilir.
kullanıcı bilinmiyor

9
"Scala sizi ve ekibinizi yaparsa ..." Alt satır: Bunu daha önce değil sonra
anlayacaksınız

Kıyaslama oyunu Yardım sayfası, "2 dil uygulaması için program hızını ve boyutunu karşılaştırma" örneğini sunar. Scala ve Java için uygun karşılaştırma web sayfası - shootout.alioth.debian.org/u64q/scala.php
igouy

20

Diğerleri, Rex Kerr'in yorum yaptığım örnekleri arasında belirgin bir performans farkı var gibi görünse de, bu soruyu sıkı döngülerle cevapladılar.

Bu cevap gerçekten tasarım hatası olarak sıkı döngü optimizasyonu ihtiyacını araştırabilecek insanları hedefliyor.

Scala için nispeten yeniyim (yaklaşık bir yıl kadar) ama şimdiye kadarki hissi , tasarım, uygulama ve yürütmenin birçok yönünü nispeten kolay bir şekilde ertelemenize izin veriyor (yeterli arka plan okuma ve deneme ile :)

Ertelenmiş Tasarım Özellikleri:

Ertelenmiş Uygulama Özellikleri:

Ertelenmiş Yürütme Özellikleri: (üzgünüm, bağlantı yok)

  • İş parçacığı için güvenli tembel değerler
  • Geçiş-adıyla
  • Monadic şeyler

Bu özellikler, bana göre, hızlı ve sıkı uygulamalara giden yolu izlememize yardımcı olan özellikler.


Rex Kerr'ın örnekleri, infazın ertelenmesinde farklıdır. Java örneğinde, bellek tahsisi, Scala örneğinin eşleme aramasını tanımladığı boyut hesaplanıncaya kadar ertelenir. Bana göre tamamen farklı algoritmalar gibi görünüyorlar.

İşte onun daha fazla elma onun elma örneği için elma eşdeğer olduğunu düşünüyorum:

val bigEnough = array.collect({
    case k: String if k.length > 2 && mapping.contains(k) => mapping(k)
})

Arada herhangi bir koleksiyon, hiçbir Optionvb örnekleri Bu aynı zamanda o kadar toplama türünü korur bigEnough'ın türüdür Array[File]- Array' ın collectuygulanması muhtemelen Bay Kerr Java kodu ne çizgisinde bir şey yapacağız.

Yukarıda listelediğim ertelenmiş tasarım özellikleri, Scala'nın koleksiyon API geliştiricilerinin API'yi bozmadan gelecekteki sürümlerde bu hızlı Array'a özgü toplama uygulamasını uygulamasına da izin verecek. Bu, hıza giden yolu işlerken bahsettiğim şey.

Ayrıca:

val bigEnough = array.withFilter(_.length > 2).flatMap(mapping.get)

withFilterBen yerine burada kullandım bu yöntem filterdüzeltmeleri ara toplama sorunu ancak Opsiyon örneği sorunu hala var.


Scala'daki basit yürütme hızının bir örneği günlük kaydıdır.

Java'da şöyle bir şey yazabiliriz:

if (logger.isDebugEnabled())
    logger.debug("trace");

Scala'da bu sadece:

logger.debug("trace")

çünkü Scala'da hata ayıklamak için kullanılan ileti parametresi, " => String" değerlendirildiğinde çalıştırılan parametresiz bir işlev olarak düşündüğüm, ancak belgelerin adlarını çapraz olarak çağırdığı " " türüne sahiptir .

EDIT {Scala'daki işlevler nesnedir, bu nedenle burada fazladan bir nesne vardır. Benim işim için, önemsiz bir nesnenin ağırlığı, bir günlük iletisinin gereksiz yere değerlendirilme olasılığını ortadan kaldırmaya değer. }

Bu, kodu daha hızlı yapmaz, ancak daha hızlı olma olasılığını artırır ve diğer insanların kodlarını toplu olarak temizleme deneyimine sahip olma olasılığımız daha düşüktür.

Bana göre, bu Scala içinde tutarlı bir temadır.


Sabit kod, Scala'nın neden daha hızlı olduğunu, ancak biraz ipucu vermediğini yakalayamıyor.

Scala'da kod yeniden kullanımının ve kod kalitesinin tavanının bir kombinasyonu olduğunu hissediyorum.

Java'da harika kod genellikle anlaşılmaz bir karmaşa haline gelmeye zorlanır ve bu nedenle çoğu programcının kullanamayacağı için üretim kalitesi API'larında gerçekten geçerli değildir.

Scala'nın aramızdaki einsteinlerin potansiyel olarak DSL'ler aracılığıyla ifade edilen çok daha yetkin API'leri uygulamasına izin verebileceğine dair umutlarım var. Scala'daki temel API'ler zaten bu yol boyunca.


Günlük dosyalarınız Scala'nın performans tuzaklarına iyi bir örnektir: logger.debug ("trace") parametresiz işlev için yeni bir nesne oluşturur.
jcsahnwaldt Reinstate Monica

Gerçekten de - bu ilişkili noktamı nasıl etkiler?
Seth

Yukarıda bahsedilen nesneler, verimlilik uğruna şeffaf IoC kontrol yapıları yapmak için de kullanılabilir. Evet, aynı sonuç Java'da teorik olarak mümkündür, ancak kodun yazılma şeklini önemli ölçüde etkileyen / gizleyen bir şey olurdu - bu nedenle Scala'nın yazılım geliştirmenin birçok öğesini erteleme konusundaki beceriksizliğinin daha hızlı koda gitmemize yardımcı olması - daha muhtemel olması uygulamada daha hızlı ve marjinal olarak daha hızlı birim performansı
Seth

Tamam, bunu tekrar okudum ve "basit yürütme hızı" yazdım - bir not ekleyeceğim. İyi nokta :)
Seth

3
Nesne tahsisi + çöpe karşı deyim (temel olarak bir süperskalar işlemcide ücretsiz) tahmin edilebilir. Java kodu açıkça daha hızlıdır (yalnızca durumu değerlendirdiğini, yürütmenin günlük deyimine ulaşamayacağını unutmayın). "İşim için önemsiz bir nesnenin ağırlığı, bir günlük iletisinin gereksiz yere değerlendirilme olasılığını ortadan kaldırmaya değer. ."
Eloff


10

Java ve Scala'nın her ikisi de JVM bayt koduna kadar derlenir, bu yüzden fark o kadar büyük değildir. Alabileceğiniz en iyi karşılaştırma, muhtemelen Java ve Scala'nın aynı bellek kullanımına sahip olduğunu söyleyen bilgisayar dili kıyaslama oyunudur . Scala, listelenen bazı ölçütlerde Java'dan biraz daha yavaştır, ancak bunun nedeni programların uygulanmasının farklı olması olabilir.

Gerçekten de, ikisi de o kadar yakın ki endişelenmeye değmez. Scala gibi daha etkileyici bir dil kullanarak elde ettiğiniz verimlilik artışı, minimum (varsa) performans vuruşundan çok daha değerli.


7
Burada mantıklı bir yanlışlık görüyorum: Her iki dil de bayt koduna kadar derleniyor, ancak deneyimli bir programcı ve bir acemi - kodları da bayt koduna kadar derleniyor - ama aynı bayt koduna değil, sonuç olarak, fark o kadar büyük olamaz , yanlış olabilir. Aslında, eski zamanlarda, bir while döngüsü, semantik olarak eşdeğer bir for döngüsünden çok, çok daha hızlı olabilir (doğru hatırlarsam, bugün çok daha iyidir). Ve elbette her ikisi de bayt koduna derlendi.
kullanıcı bilinmiyor

@user unknown - "bir while döngüsü, skalada semantik olarak eşdeğer bir for döngüsünden çok, çok daha hızlı olabilir" - bu Scala benchmark oyun programlarının while döngüleri ile yazıldığına dikkat edin.
igouy

@igouy: Bu mikrobenchmarkın sonuçları hakkında değil, tartışma hakkında konuştum. Söz konusu ifadeyle Java and Scala both compile down to JVM bytecode, birleştirilen gerçek bir ifade göstermek istedim, bunun sadece retorik bir hile olduğunu ve tartışmacı bir sonuç olmadığını. sodiffence isn't that big.so
kullanıcı bilinmiyor

3
şaşırtıcı derecede yanlış oylar şaşırtıcı derecede yüksek oylarla.
shabunc

4

Java örneği, tipik uygulama programları için bir deyim değildir. Bu tür optimize edilmiş kod, bir sistem kütüphanesi yönteminde bulunabilir. Ancak daha sonra doğru türde bir dizi kullanır, yani File [] ve bir IndexOutOfBoundsException oluşturmaz. (Sayma ve ekleme için farklı filtre koşulları). Benim sürümüm (her zaman (!) Kıvırcık parantez ile olurdu çünkü Eclipse tek bir tuşa vurmak için 2 saniye kaydederek tanıtılan bir hata aramak için bir saat harcamak istemiyorum):

List<File> bigEnough = new ArrayList<File>();
for(String s : array) {
  if(s.length() > 2) {
    File file = mapping.get(s);
    if (file != null) {
      bigEnough.add(file);
    }
  }
}

Ama size şu anki projemden başka çirkin Java kodu örnekleri getirebilirim. Ortak yapıları ve davranışları çarpanlarına ayırarak ortak kodlama ve kodlama stilini değiştirmeye çalıştım.

Soyut DAO temel sınıfımda ortak önbellek mekanizması için soyut bir iç sınıf var. Her somut model nesne tipi için soyut DAO temel sınıfının bir alt sınıfı vardır, burada iç sınıf, veritabanından yüklendiğinde iş nesnesini oluşturan yöntem için bir uygulama sağlamak üzere alt sınıflandırılır. (Özel bir API aracılığıyla başka bir sisteme eriştiğimiz için ORM aracını kullanamayız.)

Bu alt sınıflandırma ve örnekleme kodu Java'da net değildir ve Scala'da çok okunabilir olacaktı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.