Статьи

Java 8 с точки зрения производительности

Как разработчик программного обеспечения, меня больше интересуют новые функции или улучшения скорости, безопасности, масштабируемости и т. Д. По этой причине я провел несколько тестов с некоторыми функциями Java 8.

Takeaways:

  • При обходе List for ( int i = 0; i < numbers .size (); i ++) намного быстрее, чем любые другие, как в параллельных, так и в последовательных вычислениях, хотя выглядит так некрасиво. Вы можете уменьшить загрязнение кода в сочетании с Predicate и повысить возможность повторного использования за счет потери некоторой скорости, но все же быстрее, чем других, хотя и не самой быстрой. Итак, ваш выбор зависит от того, насколько вам важна скорость.
  • При обходе Map Java 8 работает быстрее, но не очень. В целом, Java 8 выигрывает и в коде, и в производительности.
  • Функция по умолчанию не выполняет никакой разницы.

1. Список хода с лямбда-выражением VS без


import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.elasticsearch.common.netty.util.internal.ThreadLocalRandom;

public class TestLambdaList {
final static List<Integer> numbers = new ArrayList<Integer>();
static int nThreads=4;
static ExecutorService es=Executors.newFixedThreadPool(nThreads);

static void prepare() {
ThreadLocalRandom tlr = ThreadLocalRandom.current();
int c = 10_000_000;
while (--c >= 0) {
numbers.add(tlr.nextInt(10_0000));
}
}

static long countParallel8(){
long s = System.currentTimeMillis();
long t = numbers.parallelStream().filter(e -> (e & 1) == 1).count();
long dif = System.currentTimeMillis() - s;
return dif;
}
static void countParallelOld(long difLambda){
long s = System.currentTimeMillis();
int step=numbers.size()/nThreads;
List<Future<Long>> fs=new ArrayList<Future<Long>>();;
for(int i=0;i<nThreads;i++){
int start=i*step;
int end=(i+1)*step;
if(i==nThreads-1){
end=numbers.size();
}
final int endEnclosed=end;
Future<Long> f=es.submit(new Callable<Long>(){
public Long call() throws Exception {
long t=0;
for(int j=start;j<endEnclosed;j++){
if ((numbers.get(j) & 1) == 1) {
t++;
}
}
return t;
}
});
fs.add(f);
}
long t=0;
for(int i=0;i<fs.size();i++){
try {
t+=fs.get(i).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
long dif = System.currentTimeMillis() - s;
System.out.println(Math.round(100.0*difLambda/dif)+"\tStream Parallel/(Old Parallel)\t");
}
static long countOddInNovelWay() {
long s = System.currentTimeMillis();
long t = numbers.stream().filter(e -> (e & 1) == 1).count();
long dif = System.currentTimeMillis() - s;
return dif;
}

static void countOddInOldWay(long difLambda) {
long s = System.currentTimeMillis();
long t = 0;
for (int i = 0; i < numbers.size(); i++) {
if ((numbers.get(i) & 1) == 1) {
t++;
}
}
long dif=System.currentTimeMillis()-s;
System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Looping like array)\t");

combine(difLambda,i->(i & 1)==1);

s = System.currentTimeMillis();
t=0;
for (Iterator<Integer> it = numbers.iterator(); it.hasNext();) {
if ((it.next() & 1) == 1) {
t++;
}
}
dif=System.currentTimeMillis()-s;
System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Looping via Iterator)\t");

s = System.currentTimeMillis();
t=0;
for (int n : numbers) {
if ((n & 1) == 1) {
t++;
}
}
dif=System.currentTimeMillis()-s;
System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Looping via for each syntax)\t");

s = System.currentTimeMillis();
t=0;
for (ListIterator<Integer> iter = numbers.listIterator(); iter.hasNext(); ) {
if ((iter.next() & 1) == 1) {
t++;
}
}
dif=System.currentTimeMillis()-s;
System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Looping via ListIterator)\t");
}
static void combine(long difLambda, Predicate<Integer> p){
long s = System.currentTimeMillis();
long t = 0;
for (int i = 0; i < numbers.size(); i++) {
if (p.idOdd(i)) {
t++;
}
}
long dif=System.currentTimeMillis()-s;
System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Looping Array-Like Combined with Predicate)\t");
}
static interface Predicate<Integer>{
boolean idOdd(Integer i);
}
public static void main(String[] args) {
prepare();
for (int i = 0; i < 10_000; i++) {
//long r=countOddInNovelWay();
//countOddInOldWay(r);

long r=countParallel8();
countParallelOld(r);
}
}
}


Results: (The number is a speed ratio of new way to old way )
298Lambda/(Looping like array)
189Lambda/(Looping Array-Like Combined with Predicate)
99Lambda/(Looping via Iterator)
90Lambda/(Looping via for each syntax)
100Lambda/(Looping via ListIterator)
198Lambda/(Looping like array)
160Lambda/(Looping Array-Like Combined with Predicate)
89Lambda/(Looping via Iterator)
100Lambda/(Looping via for each syntax)
83Lambda/(Looping via ListIterator)
198Lambda/(Looping like array)
160Lambda/(Looping Array-Like Combined with Predicate)
89Lambda/(Looping via Iterator)
80Lambda/(Looping via for each syntax)
92Lambda/(Looping via ListIterator)

488Stream Parallel/(Old Parallel)
252Stream Parallel/(Old Parallel)
488Stream Parallel/(Old Parallel)
252Stream Parallel/(Old Parallel)
232Stream Parallel/(Old Parallel)
488Stream Parallel/(Old Parallel)
488Stream Parallel/(Old Parallel)
291Stream Parallel/(Old Parallel)
279Stream Parallel/(Old Parallel)
463Stream Parallel/(Old Parallel)
252Stream Parallel/(Old Parallel)
321Stream Parallel/(Old Parallel)

2. Карта хода с лямбда-выражением VS без

Map.forEach и Map.compute работают лучше, чем старый, но не очень, но все же круто.


import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.atomic.LongAdder;

import org.elasticsearch.common.netty.util.internal.ThreadLocalRandom;

public class TestLambdaMap {
static Map<Integer, Integer> numbers=new HashMap<Integer,Integer>();
static void prepare(){
ThreadLocalRandom tlr = ThreadLocalRandom.current();
int c = 3_000_000;
while (--c >= 0) {
numbers.put(c,tlr.nextInt(10_0000));
}
}
static final LongAdder la=new LongAdder();
static long countByLambda(boolean compute){
long s = System.currentTimeMillis();
numbers.forEach((k, v)->{
if(compute){
numbers.compute(k, (Integer key, Integer val)->val*2);
}else{
if((v & 1)==1)la.increment();
}
});
long dif = System.currentTimeMillis() - s;
return dif;
}
static void countByDinosaurWay(long difLambda, boolean compute){
la.reset();
long s = System.currentTimeMillis();
for (Integer key : numbers.keySet()) {
Integer v=numbers.get(key);
if(compute){
numbers.put(key, v*2);
}else{
if((v & 1)==1)la.increment();
}
}
long dif = System.currentTimeMillis() - s;
System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Dinosaur #1)\t"+compute);

la.reset();
s = System.currentTimeMillis();
Iterator<Integer> keySetIterator = numbers.keySet().iterator();
while (keySetIterator.hasNext()) {
Integer key=keySetIterator.next();
Integer v=numbers.get(key);
if(compute){
numbers.put(key, v*2);
}else{
if((v & 1)==1)la.increment();
}
}
dif = System.currentTimeMillis() - s;
System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Dinosaur #2)\t"+compute);

la.reset();
s = System.currentTimeMillis();
for(Map.Entry<Integer, Integer> en:numbers.entrySet()){
Integer v=en.getValue();
if(compute){
numbers.put(en.getKey(), v*2);
}else{
if((v & 1)==1)la.increment();
}
}
dif = System.currentTimeMillis() - s;
System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Dinosaur #3)\t"+compute);
}
public static void main(String[] args) {
prepare();
for (int i = 0; i < 10000; i++) {
long r=countByLambda(true);
countByDinosaurWay(r,true);

r=countByLambda(false);
countByDinosaurWay(r,false);
}
}

}


Result: (The number is a speed ratio of new way to old way )
56Lambda/(Dinosaur #1)false
56Lambda/(Dinosaur #2)false
68Lambda/(Dinosaur #3)false
60Lambda/(Dinosaur #1)true
60Lambda/(Dinosaur #2)true
75Lambda/(Dinosaur #3)true
100Lambda/(Dinosaur #1)false
83Lambda/(Dinosaur #2)false
124Lambda/(Dinosaur #3)false
59Lambda/(Dinosaur #1)true
60Lambda/(Dinosaur #2)true
60Lambda/(Dinosaur #3)true
53Lambda/(Dinosaur #1)false
52Lambda/(Dinosaur #2)false
78Lambda/(Dinosaur #3)false
66Lambda/(Dinosaur #1)true
66Lambda/(Dinosaur #2)true
66Lambda/(Dinosaur #3)true

3. Функция по умолчанию против традиционной статической функции

Никакой существенной разницы нет, это хорошо.


public class TestDefaultFunction implements DefaultFunc {

public static void staticFunc(){
long l=1024*1024*16;
long s=System.currentTimeMillis();
long t=0;
while(--l>=0){
double d=Math.sqrt(l);
t+=d;
}
long e=System.currentTimeMillis();
System.out.println(t+"\t"+(e-s)+" millis");
}

public static void main(String[] args) {
TestDefaultFunction tdf=new TestDefaultFunction();
for (int i = 0; i < 100_0000; i++)
//TestDefaultFunction.staticFunc();
tdf.defaultFunc();
}
}
 interface DefaultFunc{
default void defaultFunc(){
long l=1024*1024*16;
long s=System.currentTimeMillis();
long t=0;
while(--l>=0){
double d=Math.sqrt(l);
t+=d;
}
long e=System.currentTimeMillis();
System.out.println(t+"\t"+(e-s)+" millis");
}
}