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
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.
Scala fonksiyonel Programlama Reaktif...
Neden't Python fonksiyonel progra...
Araçlar C fonksiyonel programlama için...
Yapısal programlama ve fonksiyonel pro...
Veritabanlarını ve Fonksiyonel Program...