SORU
8 Mayıs 2010, CUMARTESİ


Scala fonksiyonel programlama geleneksel kodlama daha yavaştır?

Fonksiyonel kod oluşturmak için benim ilk girişimlerden biri, bir performans sorunu ile karşılaştım.

Ortak bir görev - iki dizi elemanları çarp ve sonuçları özetle: ile başladım

var first:Array[Float] ...
var second:Array[Float] ...    
var sum=0f; 
for (ix<-0 until first.length) 
    sum  = first(ix) * second(ix);

Ben bu işe nasıl ıslah işte:

sum = first.zip(second).map{ case (a,b) => a*b }.reduceLeft(_ _)

İki yaklaşım ben karşılaştırılan, İkinci yöntem tam olarak 40 kat uzun sürer!

Neden İkinci yöntem çok daha uzun sürer? Nasıl iş hem de hızlı etkili ve işlevsel programlama kullanmak için reform tarzında olabilir miyim?

CEVAP
8 Mayıs 2010, CUMARTESİ


Bu iki örnek hızı çok farklı olmasının bir nedeni vardır:

  • daha hızlı bir herhangi bir jenerik kullanmaz, boks/kutulama yüz değil.
  • hızlı bir geçici koleksiyonlar yaratmaz ve böylece ekstra bellek kopya önler.

Hadi bir parça daha yavaş bir düşünün. İlk:

first.zip(second)

Yeni bir dizi Tuple2 Bir dizi oluşturur. Tuple2 nesneleri her iki dizide tüm unsurları kopyalamak, ve üçüncü bir diziye bu nesnelerin her biri için bir referans kopyalayın. Şimdi, Tuple2 Float doğrudan saklayabilirsiniz. böylece parametreli, dikkat edin. Bunun yerine, java.lang.Float yeni örneklerini her sayı için oluşturulan, sayıları onları saklanır, ve sonra her biri için bir referans Tuple2 içine saklanır.

map{ case (a,b) => a*b }

Şimdi dördüncü bir dizi oluşturulur. Hesaplamak için değerleri bu unsurlar, ihtiyaç duyduğu okumak için başvuru demet üçüncü dizi, okuma başvuru java.lang.Float saklı onları, okuma sayıları, çarpma, yeni java.lang.Float saklamak için neden, ve sonra geçmek bu başvuru geri çıkarde-başvurulan dizi içinde saklanabilir (dizi türü-silinmez) yeniden.

Yine de bitmedi. İşte bir sonraki bölüm:

reduceLeft(_ _)

Bu nispeten zararsız, hala reduceLeft alır beri yineleme de kutulama ve java.lang.Float oluşturma/boks yapmak dışında parametreli olan Function2,.

Scala 2.8 bu boks/kutulama çok kurtulmak edecek bir özellik ihtisas tanıttı. Ama alternatif daha hızlı sürümleri düşünün. Örneğin, tek bir adımda map reduceLeft yapabiliriz:

sum = first.zip(second).foldLeft(0f) { case (a, (b, c)) => a   b * c }

view (2.8 Km) veya projection (Scala 2.7) Ara koleksiyonlar yaratmak tamamen önlemek için kullanabiliriz:

sum = first.view.zip(second).map{ case (a,b) => a*b }.reduceLeft(_ _)

Bu sonuncusu olmayan katılık "eğer" çok hızlı, bu yöntemlerden birini bile görünümünde katı yani). kaybolmuş bence çok fazla, aslında kaydetmez, Ayrıca varsayılan olmayan katı (bazı Ara sonuçları önler ıe) sıkıştırma alternatif bir yol var:

sum = (first,second).zipped.map{ case (a,b) => a*b }.reduceLeft(_ _)

Bu eski çok daha iyi sonuç verir. foldLeft daha iyi, ama çok değil. Ne yazık ki, eski ikinci desteklemediği için foldLeft* *32 kombine edemeyiz.

Son bir tane daha alabilirim en hızlısıdır. Bundan daha hızlı, sadece uzmanlık ile. Şimdi, Function2 uzman olmak, ama Int, Long Double olur. Diğer temel öğeler dışında kalan, uzmanlık boyutunu artırır oldukça dramatik her ilkel kodu olarak. Benim testlerde, ama Double aslında daha uzun sürüyor. Bu sonuç iki katı büyüklüğünde olması olabilir, ya da yanlış yaptığım bir şey olabilir.

Böylece, sonunda, sorun etkenler, elemanları, ve Java (JVM) ilkel ve jenerik işleme aracı kopyalarını üreten dahil olmak üzere. Haskell benzer bir kod supercompilation kullanarak bir şey çevirici kısa eşit olacaktır. JVM, farkında takaslar olacak ve kritik kod optimize etmek için hazır olmak zorunda.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Bokeh

    Bokeh

    9 HAZİRAN 2014
  • LavcoPriceTech

    LavcoPriceTe

    21 AĞUSTOS 2010
  • WOSU Public Media

    WOSU Public

    23 AĞUSTOS 2007