はじめに
Java SE 8のIntStream.sum()の性能を測定してみました。
IntStream.sum()は、並列処理のコストが低く、並列化すれば性能が向上するだろうという期待がありました。
性能測定
1~100,000の総和($\sum_{i=1}^n i$)を求めます。
次の3通りの処理方法における処理時間を出力します。
- 単純なfor文で総和を計算
- IntStream.sum()で計算
- IntStream.sum()を並列で計算
プログラム
Sum.java
public class Sum {
private static final int SIZE = 100_000;
public static void main(String[] args){
System.out.println("os.arch="+System.getProperty("os.arch"));
System.out.println("os.name="+System.getProperty("os.name"));
System.out.println("os.version="+System.getProperty("os.version"));
System.out.println("java.version="+System.getProperty("java.version"));
System.out.println("java.vm.info="+System.getProperty("java.vm.info"));
System.out.println("java.vm.name="+System.getProperty("java.vm.name"));
System.out.println("sun.management.compiler="+System.getProperty("sun.management.compiler"));
System.out.println("poolParallelism="+java.util.concurrent.ForkJoinPool.getCommonPoolParallelism());
//warming up
for(int i=0; i<10_000; i++){
createIntStream().sum();
createIntStream().parallel().sum();
}
//process
System.out.println();
sum000();
sum001();
sum002();
}
private static java.util.stream.IntStream createIntStream(){
return java.util.stream.IntStream.rangeClosed(1, SIZE);
}
static void sum000(){
final long start = System.nanoTime();
int sum = 0;
for(int i=0; i<=SIZE; i++){
sum += i;
}
result("sum000", sum, start);
}
static void sum001(){
final long start = System.nanoTime();
result("sum001", createIntStream().sum(), start);
}
static void sum002(){
final long start = System.nanoTime();
result("sum002", createIntStream().parallel().sum(), start);
}
static void result(String prefix, int sum, long start){
final long elapsed = System.nanoTime()-start;
System.out.println(prefix+": sum="+sum+", elapsed="+elapsed);
}
}
測定環境
Key | Value |
---|---|
CPU | Intel Core i7-2670QM CPU @ 2.20GHz コア数4 スレッド数8 |
RAM | 8GB |
OS | Windows 7 Home Premium SP1 64ビット版 |
Java | Java SE 8 Update 20 |
測定結果
os.arch=amd64
os.name=Windows 7
os.version=6.1
java.version=1.8.0_20
java.vm.info=mixed mode
java.vm.name=Java HotSpot(TM) 64-Bit Server VM
sun.management.compiler=HotSpot 64-Bit Tiered Compilers
poolParallelism=7
- 1回目
- sum000: sum=705082704, elapsed=3846275
- sum001: sum=705082704, elapsed=113359
- sum002: sum=705082704, elapsed=429644
- sum001: sum=705082704, elapsed=113359
- 2回目
- sum000: sum=705082704, elapsed=1785288
- sum001: sum=705082704, elapsed=50848
- sum002: sum=705082704, elapsed=229050
- sum001: sum=705082704, elapsed=50848
- 3回目
- sum000: sum=705082704, elapsed=2575069
- sum001: sum=705082704, elapsed=74173
- sum002: sum=705082704, elapsed=332147
- sum001: sum=705082704, elapsed=74173
まとめ
- あらかじめウォーミングアップしておけば、単純なfor文よりもStreamを使った方が性能が良くなりました。
- ただし、sum()をパラレルで実行すると、シーケンスよりも性能が悪化しました。(原因はこれから検証する)
- 要素数を10万→1000万に増やしても(10万でも1000万でも総和がオーバーフローするが)、パラレル処理の性能が悪かったです。
お願い
別の性能測定方法(別のプログラム)や、別の環境(CPU、コア数、OS)では、性能測定の結果が違うよという事例がありましたら、コメントをいただけると嬉しいです。