Java ile karşılaştırıldığında Scala'nın performansı


41

Her şeyden önce, bunun hangisinin daha iyi olduğunu belirlemek için bir dil-X-dil-dil-Y sorusu olmadığını açıkça belirtmek isterim.

Java'yı uzun zamandır kullanıyorum ve kullanmaya devam etmek istiyorum. Buna paralel olarak, şu anda Scala'yı büyük bir ilgi ile öğreniyorum: izlenimime alışması gereken küçük şeyler dışında, bu dilde gerçekten çok iyi çalışabileceğim.

Sorum şu: Scala'da yazılan yazılım, Java'da yazılan yazılımlarla yürütme hızı ve bellek tüketimi açısından nasıl kıyaslanıyor? Tabii ki, bu genel olarak cevaplanması zor bir sorudur, ancak desen eşleştirme, üst düzey işlevler vb.

Ancak, Scala'daki mevcut deneyimim, 50 kod satırı altındaki küçük örneklerle sınırlıdır ve şu ana kadar herhangi bir karşılaştırma yapmadım. Yani, gerçek verilerim yok.

Scala’nın bazı genel giderler Java’ya sahip olduğu ortaya çıktıysa, Scala’da daha karmaşık parçaları ve Java’da kritik performans gösteren parçaları kodlayan karışık Scala / Java projelerinin yapılması mantıklı geliyor mu? Bu yaygın bir uygulama mı?

1 EDIT

Küçük bir kıyaslama yaptım: bir tamsayı listesi oluşturun, her bir tamsayıyı iki ile çarpın ve yeni bir listeye koyun, sonuç listesini yazdırın. Bir Java uygulaması (Java 6) ve Scala uygulaması (Scala 2.9) yazdım. İkisini de Eclipse Indigo'da Ubuntu 10.04 altında çalıştırdım.

Sonuçlar karşılaştırılabilir: Java için 480 ms ve Scala için 493 ms (100 yineleme üzerinden ortalama). İşte kullandığım pasajlar.

// Java
public static void main(String[] args)
{
    long total = 0;
    final int maxCount = 100;
    for (int count = 0; count < maxCount; count++)
    {
        final long t1 = System.currentTimeMillis();

        final int max = 20000;
        final List<Integer> list = new ArrayList<Integer>();
        for (int index = 1; index <= max; index++)
        {
            list.add(index);
        }

        final List<Integer> doub = new ArrayList<Integer>();
        for (Integer value : list)
        {
            doub.add(value * 2);
        }

        for (Integer value : doub)
        {
            System.out.println(value);
        }

        final long t2 = System.currentTimeMillis();

        System.out.println("Elapsed milliseconds: " + (t2 - t1));
        total += t2 - t1;
    }

    System.out.println("Average milliseconds: " + (total / maxCount));
}

// Scala
def main(args: Array[String])
{
    var total: Long = 0
    val maxCount    = 100
    for (i <- 1 to maxCount)
    {
        val t1   = System.currentTimeMillis()
        val list = (1 to 20000) toList
        val doub = list map { n: Int => 2 * n }

        doub foreach ( println )

        val t2 = System.currentTimeMillis()

        println("Elapsed milliseconds: " + (t2 - t1))
        total = total + (t2 - t1)
    }

    println("Average milliseconds: " + (total / maxCount))
}

Dolayısıyla, bu durumda Scala ek yükünün (aralık, harita, lambda kullanarak) gerçekten çok düşük olduğu görülüyor ve bu da World Engineer tarafından sağlanan bilgilerden çok uzak değil.

Belki de yürütülmesi özellikle ağır olduğu için dikkatle kullanılması gereken başka Scala yapıları vardır.

EDIT 2

Bazılarınız, iç döngüdeki baskının, yürütme zamanının çoğunu aldığını belirtti. Bunları sildim ve listelerin boyutunu 20000 yerine 100000 olarak belirledim. Elde edilen ortalama Java için 88 ms ve Scala için 49 ms idi.


5
Scala'nın JVM bayt kodunu derlediği için, performansın teorik olarak aynı JVM altında çalışan Java'ya eşdeğer olabileceğini düşünüyorum. Sanırım fark, Scala derleyicisinin bayt kodunu nasıl oluşturduğu ve eğer böyle bir şekilde yapıp yapmadığı.
maple_shaft

2
@maple_shaft: Ya da Scala derleme süresinde genel gider olabilir?
SinirliFormsDesigner'la

1
@Giorgio Scala nesneleri ve Java nesneleri arasında çalışma zamanı ayrımı yoktur, hepsi bayt kodu başına tanımlanmış ve davranan JVM nesneleridir. Örneğin bir dil olarak Scala'nın kapanma konsepti vardır, ancak bunlar derlendiğinde bayt kodlu birkaç sınıfa derlenir. Teorik olarak, tam olarak aynı bayt kodunu derleyebilecek fiziksel olarak Java kodu yazabilirim ve çalışma zamanı davranışı tamamen aynı olurdu.
maple_shaft

2
@maple_shaft: Tam olarak neyi hedefliyorum: Yukarıdaki Scala kodunu, ilgili Java kodundan çok daha özlü ve okunabilir buluyorum. Java’da bir Scala projesinin bölümlerini performans nedenleriyle ve bu bölümlerin ne olacağını yazmanın mantıklı olup olmadığını merak ediyordum.
Giorgio

2
Çalışma zamanı büyük ölçüde println çağrıları tarafından işgal edilir. Daha fazla bilgi işlem gerektiren bir teste ihtiyacınız var.
kevin cline

Yanıtlar:


39

Java’da Scala’da yapamayacağınız özlü ve verimli bir şekilde yapabileceğiniz bir şey var: numaralandırmalar. Her şey için, Scala'nın kütüphanesinde yavaş olan yapılar için bile, Scala'da çalışan verimli sürümler elde edebilirsiniz.

Bu nedenle, çoğunlukla, kodunuza Java eklemeniz gerekmez. Java'da numaralandırma kullanan kod için bile, genellikle Scala'da yeterli veya iyi bir çözüm vardır - Ekstra yöntemleri olan ve int sabit değerleri kullanılan numaralandırmalara istisna yapıyorum.

Dikkat edilmesi gerekenler ise, işte bazı şeyler.

  • Kütüphane düzenimi zenginleştiriyorsanız, daima bir sınıfa dönüştürün. Örneğin:

    // WRONG -- the implementation uses reflection when calling "isWord"
    implicit def toIsWord(s: String) = new { def isWord = s matches "[A-Za-z]+" }
    
    // RIGHT
    class IsWord(s: String) { def isWord = s matches "[A-Za-z]+" }
    implicit def toIsWord(s: String): IsWord = new IsWord(s)
    
  • Toplama yöntemlerine karşı dikkatli olun - çünkü çoğunlukla polimorfik olduklarından JVM bunları optimize etmiyor. Onlardan kaçınmanıza gerek yok, ancak kritik bölümlerde buna dikkat edin. forScala'da yöntem çağrıları ve anonim sınıflar aracılığıyla uygulandığına dikkat edin .

  • Gibi bir Java sınıfı kullanılıyorsa String, Arrayya da AnyValalternatifler varken sınıfları Java ilkel o karşılıklıdır Java tarafından sağlanan yöntemleri tercih ediyorum. Örneğin, kullanmak lengthüzerine Stringve Arrayyerine size.

  • Kendinizi tasarımdan ziyade yanlışlıkla yanlış kullanmaya başlayabildiğiniz için, gizli dönüşümleri dikkatsiz kullanmaktan kaçının.

  • Özellikleri yerine sınıfları genişletin. Örneğin, uzatıyorsanız Function1, AbstractFunction1bunun yerine uzatın .

  • -optimiseScala'nın çoğundan yararlanmak için uzmanlık kullanın .

  • Neler olduğunu anlayın: javapArkadaşınız ve neler olduğunu gösteren bir sürü Scala bayrağı.

  • Scala deyimleri doğruluğu artırmak ve kodu daha kısa ve sürdürülebilir hale getirmek için tasarlanmıştır. Hız için tasarlanmamışlardır, yani kritik bir yol nullyerine kullanmanız gerekirse Option, yapın! Scala'nın çoklu paradigma olmasının bir nedeni var.

  • Unutmayınız ki performans ölçütü kod çalıştırıyordur. Bu kuralı görmezden gelirseniz neler olabileceğine dair bir örnek için bu soruya bakın .


1
+1: Hala öğrenmem gereken konular hakkında bile pek çok yararlı bilgi var, ancak onlara bakmadan önce bazı ipuçlarını okumak faydalıdır.
Giorgio

Neden ilk yaklaşım yansıma kullanıyor? Yine de adsız bir sınıf oluşturur, öyleyse neden yansıma yerine kullanmıyorsun?
Oleksandr.Bezhan

@ Oleksandr.Bezhan Anonim sınıf bir Scala değil, bir Java kavramdır. Bir tür iyileştirme oluşturur. Temel sınıfını geçersiz kılmayan anonim bir sınıf yöntemine dışarıdan erişilemez. Aynısı Scala'nın tip iyileştirmeleri için de geçerli değil, bu yüzden bu metoda ulaşmanın tek yolu yansımadır.
Daniel C. Sobral

Bu oldukça korkunç geliyor. Özellikle: "Toplama yöntemlerine karşı dikkatli olun - çünkü bunlar çoğunlukla polimorfiktir, JVM bunları optimize etmez. Onlardan kaçınmanıza gerek kalmaz, ancak kritik bölümlerde buna dikkat edin."
Matt

21

Tek çekirdekli Benchmarks Game'e göre , 32 bit sistemde, Scala, Java kadar hızlı bir şekilde% 80 oranında. Performans, bir Quad Core x64 bilgisayar için yaklaşık olarak aynıdır. Bellek kullanımı ve kod yoğunluğu bile çoğu durumda çok benzerdir. Scala'nın Java'ya bir miktar ek eklediğini iddia etmekte haklı olduğunuzu söyleyen (bilimsel olmayan) analizlere dayanarak söyleyebilirim. Tonlarca ek yük görünmüyor, bu yüzden daha fazla yer / zaman alan yüksek mertebeden öğelerin teşhisinin en doğru olanı olduğundan şüpheleniyorum.


2
Bu cevap için, lütfen Yardım sayfasının önerdiği şekilde doğrudan karşılaştırmayı kullanın ( shootout.alioth.debian.org/help.php#comparetwo )
igouy

18
  • Scala'da Java / C benzeri bir kod yazarsanız, Scala performansı çok iyidir. Derleyici için JVM ilkeller kullanacak Int, Charne zaman can vb. Scala'da döngüler de aynı derecede verimli.
  • Lamda ifadelerinin, Functionsınıfların adsız alt sınıfları için derlendiğini unutmayın . Bir lambda iletirseniz map, anonim sınıfın başlatılması gerekir (ve bazı yerlilerin geçmesi gerekebilir) ve sonra her yinelemede çağrılardan ek fonksiyon çağrısı ek yükü (bazı parametrelerin geçmesiyle) bulunur apply.
  • Gibi birçok sınıf scala.util.Randomeşdeğer JRE sınıflarının etrafındaki sarmalayıcılardır. Ekstra işlev çağrısı biraz israf olur.
  • Performans kritik kodundaki imalara dikkat edin. geri dönüştüren ve dönüştürdüğünden java.lang.Math.signum(x)çok daha doğrudan .x.signum()RichInt
  • Scala'nın Java'ya göre ana performans avantajı uzmanlıktır. Uzmanlığın az miktarda kütüphane kodunda kullanıldığını unutmayın.

5
  • a) Sınırlı bilgimden, şunu söylemeliyim ki, statik ana yöntemdeki kod çok iyi optimize edilemez. Kritik kodu farklı bir yere taşımalısınız.
  • b) Uzun gözlemlerden, performans testinde ağır çıktılar yapılmamasını tavsiye ederim (tam olarak optimize etmek istediğiniz şey değil, fakat kim 2 Milyon değer okumalı?). Çok ilginç olmayan baskıyı ölçüyorsun. Println'yi max ile değiştirmek:
(1 to 20000).toList.map (_ * 2).max

sistemimde zamanı 800 ms'den 20'ye düşürüyor.

  • c) Anlamanın biraz yavaş olduğu bilinmektedir (her zaman daha da iyi olduğunu kabul etmek zorunda kalırsak). Bunun yerine while veya tailrecursive işlevlerini kullanın. Bu örnekte değil, dış döngü olduğu yerde. Tairecursivity test etmek için @ tailrec-notunu kullanın.
  • d) C / Assembler ile karşılaştırma başarısız. Örneğin, farklı mimariler için scala kodunu yeniden yazmazsınız. Tarihsel durumlar için diğer önemli farklar
    • JIT-derleyici, anında optimize ve giriş verilerine bağlı olarak dinamik olarak
    • Önbellek özlelerinin önemi
    • Paralel çağrılamanın artan önemi. Scala bugün paralel olarak fazla ek harcamadan çalışmak için çözümlere sahip. Java'da mümkün değil, daha çok iş yapman dışında.

2
Println'i döngüden kaldırdım ve aslında Scala kodu Java kodundan daha hızlı.
Giorgio

C ve Assembler ile karşılaştırmak şu anlama gelmekteydi: daha yüksek seviyeli bir dil daha güçlü soyutlamalara sahiptir ancak performans için daha düşük seviyeli dili kullanmanız gerekebilir. Bu paralel Scala'yı en yüksek seviye ve Java'yı alt seviye dili olarak kabul ediyor mu? Belki de değildir, çünkü Scala, Java'ya benzer bir performans sergiliyor gibi görünüyor.
Giorgio

Clojure veya Scala için çok önemli olacağını düşünmemiştim ama jRuby ve Jython ile oynadığımda muhtemelen Java'da daha yüksek performans kritik kodu yazmış olurdum. Bu ikisiyle kayda değer bir eşitsizlik gördüm ama bu yıllar önceydi şimdi ... daha iyi olabilirdi.
Rig
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.