我爲10,000,000個元素運行了一組性能基準,並且我發現每個實現的結果都有很大不同。scala範圍與大型集合上的性能對比列表
任何人都可以解釋爲什麼創建一個Range.ByOne,導致性能比簡單的基元數組更好,但是將相同的範圍轉換爲列表會導致比最壞情況更糟的性能?
創建10,000,000個元素,並打印出模數爲1,000,000的元素。 JVM大小總是設置爲相同的最小值和最大值:??-Xms米-Xmx米
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeUnit._
object LightAndFastRange extends App {
def chrono[A](f: => A, timeUnit: TimeUnit = MILLISECONDS): (A,Long) = {
val start = System.nanoTime()
val result: A = f
val end = System.nanoTime()
(result, timeUnit.convert(end-start, NANOSECONDS))
}
def millions(): List[Int] = (0 to 10000000).filter(_ % 1000000 == 0).toList
val results = chrono(millions())
results._1.foreach(x => println ("x: " + x))
println("Time: " + results._2);
}
它需要141毫秒與JVM尺寸2700萬
相比之下,轉換成列表影響性能大幅提升:
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeUnit._
object LargeLinkedList extends App {
def chrono[A](f: => A, timeUnit: TimeUnit = MILLISECONDS): (A,Long) = {
val start = System.nanoTime()
val result: A = f
val end = System.nanoTime()
(result, timeUnit.convert(end-start, NANOSECONDS))
}
val results = chrono((0 to 10000000).toList.filter(_ % 1000000 == 0))
results._1.foreach(x => println ("x: " + x))
println("Time: " + results._2)
}
它需要8514-10896毫秒460-455米
與此相反,該Java實現使用原語
的陣列import static java.util.concurrent.TimeUnit.*;
public class LargePrimitiveArray {
public static void main(String[] args){
long start = System.nanoTime();
int[] elements = new int[10000000];
for(int i = 0; i < 10000000; i++){
elements[i] = i;
}
for(int i = 0; i < 10000000; i++){
if(elements[i] % 1000000 == 0) {
System.out.println("x: " + elements[i]);
}
}
long end = System.nanoTime();
System.out.println("Time: " + MILLISECONDS.convert(end-start, NANOSECONDS));
}
}
這需要116ms與JVM大小59米
的Java的整數列表的
import java.util.List;
import java.util.ArrayList;
import static java.util.concurrent.TimeUnit.*;
public class LargeArrayList {
public static void main(String[] args){
long start = System.nanoTime();
List<Integer> elements = new ArrayList<Integer>();
for(int i = 0; i < 10000000; i++){
elements.add(i);
}
for(Integer x: elements){
if(x % 1000000 == 0) {
System.out.println("x: " + x);
}
}
long end = System.nanoTime();
System.out.println("Time: " + MILLISECONDS.convert(end-start, NANOSECONDS));
}
}
這需要3993毫秒JVM大小的283米
我的問題是,爲什麼第一個例子如此高效,而第二個則受到如此嚴重的影響。我嘗試創建視圖,但未能成功重現該範圍的性能優勢。
在Mac OS X Snow Leopard中運行所有測試, 的Java 6u26 64位服務器 斯卡拉2.9.1.final
編輯:
完成,這裏是一個使用LinkedList的實際執行(這在比ArrayList的空間方面更公平的比較,因爲作爲正確地指出,Scala的列表被鏈接)
import java.util.List;
import java.util.LinkedList;
import static java.util.concurrent.TimeUnit.*;
public class LargeLinkedList {
public static void main(String[] args){
LargeLinkedList test = new LargeLinkedList();
long start = System.nanoTime();
List<Integer> elements = test.createElements();
test.findElementsToPrint(elements);
long end = System.nanoTime();
System.out.println("Time: " + MILLISECONDS.convert(end-start, NANOSECONDS));
}
private List<Integer> createElements(){
List<Integer> elements = new LinkedList<Integer>();
for(int i = 0; i < 10000000; i++){
elements.add(i);
}
return elements;
}
private void findElementsToPrint(List<Integer> elements){
for(Integer x: elements){
if(x % 1000000 == 0) {
System.out.println("x: " + x);
}
}
}
}
注意到3621-6749毫秒480-460 MBS。這更符合第二個scala示例的性能。
最後,LargeArrayBuffer
import collection.mutable.ArrayBuffer
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeUnit._
object LargeArrayBuffer extends App {
def chrono[A](f: => A, timeUnit: TimeUnit = MILLISECONDS): (A,Long) = {
val start = System.nanoTime()
val result: A = f
val end = System.nanoTime()
(result, timeUnit.convert(end-start, NANOSECONDS))
}
def millions(): List[Int] = {
val size = 10000000
var items = new ArrayBuffer[Int](size)
(0 to size).foreach (items += _)
items.filter(_ % 1000000 == 0).toList
}
val results = chrono(millions())
results._1.foreach(x => println ("x: " + x))
println("Time: " + results._2);
}
以約2145毫秒和375 MB
非常感謝您的答案。
請注意,Java的'LinkedList'是雙重鏈接的,所以除了Scala的尾部引用之外,每個單元都有一個反向引用。 –
沒有必要訴諸Java代碼來使用原語。 'Array [Int]'將在封面下使用原語。等效代碼應該與Java版本的速度相同。 –
我想知道你爲什麼要測量JVM的大小,以米爲單位::-) – PhiLho