Diğer cevapların hiçbiri, hız farkının birincil nedeninden bahsetmemektedir, bu da zipped
sürümün 10.000 tuple tahsisinden kaçınmasıdır. Diğer cevaplar bir çift olarak yapmak notu zip
ise versiyon, bir ara dizi içerir zipped
sürüm değil, ama 10.000 öğeleri için bir dizi tahsis kılan değil zip
versiyonunu çok daha kötü-10.000 kısa ömürlü tuples var olduğunu bu diziye konuluyor. Bunlar JVM üzerindeki nesneler tarafından temsil edilir, bu yüzden hemen atacağınız şeyler için bir grup nesne tahsisi yapıyorsunuz.
Bu cevabın geri kalanı bunu nasıl doğrulayabileceğiniz hakkında biraz daha ayrıntıya giriyor.
Daha iyi kıyaslama
JVM'de sorumlu bir şekilde herhangi bir kıyaslama yapmak için jmh gibi bir çerçeve kullanmak istiyorsunuz ve o zaman bile jmh'yi kurmak çok kötü olmasa da sorumlu kısım zor. Eğer böyle bir şey varsa project/plugins.sbt
:
addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.3.7")
Ve bunun build.sbt
gibi (kullandığınız şeyden bahsettiğinizden 2.11.8 kullanıyorum):
scalaVersion := "2.11.8"
enablePlugins(JmhPlugin)
Ardından kıstasınızı şu şekilde yazabilirsiniz:
package zipped_bench
import org.openjdk.jmh.annotations._
@State(Scope.Benchmark)
@BenchmarkMode(Array(Mode.Throughput))
class ZippedBench {
val arr1 = Array.fill(10000)(math.random)
val arr2 = Array.fill(10000)(math.random)
def ES(arr: Array[Double], arr1: Array[Double]): Array[Double] =
arr.zip(arr1).map(x => x._1 + x._2)
def ES1(arr: Array[Double], arr1: Array[Double]): Array[Double] =
(arr, arr1).zipped.map((x, y) => x + y)
@Benchmark def withZip: Array[Double] = ES(arr1, arr2)
@Benchmark def withZipped: Array[Double] = ES1(arr1, arr2)
}
Ve şununla çalıştır sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 zipped_bench.ZippedBench"
:
Benchmark Mode Cnt Score Error Units
ZippedBench.withZip thrpt 20 4902.519 ± 41.733 ops/s
ZippedBench.withZipped thrpt 20 8736.251 ± 36.730 ops/s
Bu, zipped
sürümün yaklaşık% 80 daha fazla verim aldığını gösterir , bu da muhtemelen ölçümlerinizle aynı veya daha az aynıdır.
Tahsislerin ölçülmesi
Ayrıca jmh'den ayırmaları ölçmesini isteyebilirsiniz -prof gc
:
Benchmark Mode Cnt Score Error Units
ZippedBench.withZip thrpt 5 4894.197 ± 119.519 ops/s
ZippedBench.withZip:·gc.alloc.rate thrpt 5 4801.158 ± 117.157 MB/sec
ZippedBench.withZip:·gc.alloc.rate.norm thrpt 5 1080120.009 ± 0.001 B/op
ZippedBench.withZip:·gc.churn.PS_Eden_Space thrpt 5 4808.028 ± 87.804 MB/sec
ZippedBench.withZip:·gc.churn.PS_Eden_Space.norm thrpt 5 1081677.156 ± 12639.416 B/op
ZippedBench.withZip:·gc.churn.PS_Survivor_Space thrpt 5 2.129 ± 0.794 MB/sec
ZippedBench.withZip:·gc.churn.PS_Survivor_Space.norm thrpt 5 479.009 ± 179.575 B/op
ZippedBench.withZip:·gc.count thrpt 5 714.000 counts
ZippedBench.withZip:·gc.time thrpt 5 476.000 ms
ZippedBench.withZipped thrpt 5 11248.964 ± 43.728 ops/s
ZippedBench.withZipped:·gc.alloc.rate thrpt 5 3270.856 ± 12.729 MB/sec
ZippedBench.withZipped:·gc.alloc.rate.norm thrpt 5 320152.004 ± 0.001 B/op
ZippedBench.withZipped:·gc.churn.PS_Eden_Space thrpt 5 3277.158 ± 32.327 MB/sec
ZippedBench.withZipped:·gc.churn.PS_Eden_Space.norm thrpt 5 320769.044 ± 3216.092 B/op
ZippedBench.withZipped:·gc.churn.PS_Survivor_Space thrpt 5 0.360 ± 0.166 MB/sec
ZippedBench.withZipped:·gc.churn.PS_Survivor_Space.norm thrpt 5 35.245 ± 16.365 B/op
ZippedBench.withZipped:·gc.count thrpt 5 863.000 counts
ZippedBench.withZipped:·gc.time thrpt 5 447.000 ms
… gc.alloc.rate.norm
Muhtemelen en ilginç kısım nerede?zip
versiyonun üç katından fazla tahsis edildiğini gösteriyor zipped
.
Zorunlu uygulamalar
Bu yöntemin son derece performansa duyarlı bağlamlarda çağrılacağını bilseydim, muhtemelen şu şekilde uygularım:
def ES3(arr: Array[Double], arr1: Array[Double]): Array[Double] = {
val minSize = math.min(arr.length, arr1.length)
val newArr = new Array[Double](minSize)
var i = 0
while (i < minSize) {
newArr(i) = arr(i) + arr1(i)
i += 1
}
newArr
}
Diğer yanıtları birinde optimize sürümü, aksine bu kullandığını unutmayın while
yerine ait for
beri for
hala Scala koleksiyonları operasyonları içine desugar edecektir. Bu uygulamayı ( withWhile
), diğer yanıtın optimize edilmiş (ancak yerinde değil) uygulamasını ( withFor
) ve iki orijinal uygulamayı karşılaştırabiliriz:
Benchmark Mode Cnt Score Error Units
ZippedBench.withFor thrpt 20 118426.044 ± 2173.310 ops/s
ZippedBench.withWhile thrpt 20 119834.409 ± 527.589 ops/s
ZippedBench.withZip thrpt 20 4886.624 ± 75.567 ops/s
ZippedBench.withZipped thrpt 20 9961.668 ± 1104.937 ops/s
Bu, zorunlu ve işlevsel sürümler arasında gerçekten büyük bir farktır ve tüm bu yöntem imzaları tamamen aynıdır ve uygulamalar aynı semantiğe sahiptir. Zorunlu uygulamalar küresel devlet vb. Kullanıyor gibi değil.zip
Ve zipped
sürümleri daha okunaklı , kişisel olarak zorunlu sürümlerin "Scala ruhuna" karşı bir anlamı olduğunu düşünmüyorum ve tereddüt etmem onları kendim kullanmak için.
Tablo ile
Güncelleme: Başka bir tabulate
yanıttaki yoruma dayanarak karşılaştırmaya bir uygulama ekledim :
def ES4(arr: Array[Double], arr1: Array[Double]): Array[Double] = {
val minSize = math.min(arr.length, arr1.length)
Array.tabulate(minSize)(i => arr(i) + arr1(i))
}
zip
Zorunlu olanlardan çok daha yavaş olmasına rağmen , sürümlerden çok daha hızlı :
Benchmark Mode Cnt Score Error Units
ZippedBench.withTabulate thrpt 20 32326.051 ± 535.677 ops/s
ZippedBench.withZip thrpt 20 4902.027 ± 47.931 ops/s
Bir işlevi çağırmanın doğası gereği pahalı bir şey olmadığı ve dizi öğelerine dizinle erişilmesi çok ucuz olduğu için beklediğim şey budur.