在我們編寫Scala代碼的時候,由于ListBuffer方便的特性,所以經(jīng)常會使用ListBuffer鼠冕。過去也一直沒有什么問題。
但是呢防症,最近在我們的項目中,出現(xiàn)了一個非常嚴重的性能問題。通過JProfiler分析告希,發(fā)現(xiàn)是調(diào)用ListBuffer的獲取數(shù)據(jù)的方法時扑浸,太慢導(dǎo)致的。
差距有多大呢燕偶?以前需要運行12個小時,改成了Java的List以后酝惧,直接縮短了四個小時伯诬。
這篇文章中,我不會具體分析ArrayBuffer/ScalaBuffer等的代碼哩陕,只會給出測試的結(jié)果赫舒。
Scala ArrayBuffer
package com.hyper
import scala.collection.JavaConverters._
import scala.collection.mutable.ArrayBuffer
object TestScalaList {
def main(args: Array[String]): Unit = {
val scalaListBuffer: ArrayBuffer[String] = ArrayBuffer[String]()
val itemNumber = 100000
for (i <- 0 until itemNumber) {
scalaListBuffer += i.toString
}
IterateList.iterate(scalaListBuffer.toList.asJava)
}
}
IterateList
的代碼如下:
package com.hyper;
import com.google.common.base.Stopwatch;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class IterateList {
public static void iterate(List<String> list) {
int listLength = list.size();
System.out.println("List length: " + listLength);
Stopwatch stopwatch = new Stopwatch();
stopwatch.start();
System.out.println("List class: " + list.getClass());
for (int i = 0; i < listLength; i++) {
list.get(i);
}
long elapse = stopwatch.elapsed(TimeUnit.SECONDS);
System.out.println("elapse: " + elapse);
}
}
運行上面的代碼心赶,我們可以看到如下結(jié)果:
運行了15s缨叫。時間不短荔燎。它的底層還是數(shù)組實現(xiàn)的呢。
Java ArrayList
那我們再來看下Java ArrayList的性能咏闪。
package com.hyper;
import java.util.ArrayList;
import java.util.List;
public class TestJavaList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
int itemNumber = 100000;
for (int i = 0; i < itemNumber; i++) {
list.add(String.valueOf(i));
}
IterateList.iterate(list);
}
}
結(jié)果如下:
僅僅用了0s,1s都不到纵装。
都是用數(shù)組實現(xiàn)的,差距咋這么大呢诗箍?
我沒有仔細探究挽唉,猜測一個是因為SeqWrapper這東西筷狼,增加了調(diào)用棧的深度埂材,進一步增加了運行時間汤求,但是這個應(yīng)該不會導(dǎo)致這么大的差距。所以更可能的原因是扬绪,Scala實現(xiàn)的這個ArrayBuffer就有問題。
有時間仔細研究一下源碼和字節(jié)碼挤牛,補充上來。
Scala ListBuffer
這個是我們Scala中最常用的數(shù)據(jù)結(jié)構(gòu)格二,Spark代碼中也有意無意的總用到這個數(shù)據(jù)結(jié)構(gòu)竣蹦。
我們都知道,Java中ArrayList和LinkedList這兩種數(shù)據(jù)結(jié)構(gòu)长窄,各有各的優(yōu)缺點纲菌,各有各的應(yīng)用場景。在Scala中嚣潜,ArrayBuffer就是ArrayList的另一個版本椅贱,而ListBuffer就是LinkedList的另一個版本。所以计技,拿ListBuffer那兩個數(shù)據(jù)結(jié)構(gòu)對比山橄,有點不公平。畢竟ListBuffer不適合隨機讀取睡雇。
但是既然做了,這里還是貼上來秕豫。
package com.hyper
import scala.collection.JavaConverters._
import scala.collection.mutable.ListBuffer
object TestScalaList {
def main(args: Array[String]): Unit = {
val scalaListBuffer: ListBuffer[String] = ListBuffer[String]()
val itemNumber = 100000
for (i <- 0 until itemNumber) {
scalaListBuffer += i.toString
}
IterateList.iterate(scalaListBuffer.toList.asJava)
}
}
結(jié)果如下:
Oh My God馁蒂,竟然運行了45s蜘腌。
總結(jié)
從測試結(jié)果中,我們可以看到沮脖,Scala代碼芯急,性能跟Java代碼還是有一定差距的。
所以能用Java還是盡量用Java為好免姿。