これは
- lombok 使うと便利なので、実際のインスタンス生成周りのパフォーマンスを調べてみた
- 結果としては、Setter感でした
※ Javaについては、7まで嫌いだったが8から普通な感じ
検査内容
jvm version 8,9 (2つ) x builder/コンストラクタ/setter(3つ)
x ThreadPool, ForkJoinPool, ParallelStream, yield (4つ) の計24パターン
について、fib計算をLongStream.range(0,30)を実行してインスタンス生成の時間計測
計測結果
8の方が処理速度早めで、Setterが最速で ThreadPool が早そう
jvm | インスタンス作成 | 実行パターン | 時間(mill sec) 3回計測 | Avg |
---|---|---|---|---|
8 | Builder | ThreadPool | 19,21,20 | 20.00 |
Parallel | 54,20,21 | 31.67 | ||
ForkJoinPool | 94,31,20 | 48.33 | ||
yield | 288,92,103 | 161 | ||
Setter | ThreadPool | 24,21,23 | 22.67 | |
Parallel | 32,25,21 | 26.00 | ||
ForkJoinPool | 25,24,24 | 24.33 | ||
yield | 104,93,94 | 97.00 | ||
Constructor | ThreadPool | 34,38,24 | 32.00 | |
Parallel | 72,46,52 | 56.67 | ||
ForkJoinPool | 38,31,21 | 30.00 | ||
yield | 114,84,88 | 95.33 | ||
9 | Builder | ThreadPool | 50,24,24 | 32.67 |
Parallel | 80,29,29 | 46.00 | ||
ForkJoinPool | 23,27,26 | 25.33 | ||
yield | 126,103,98 | 109.00 | ||
Setter | ThreadPool | 23,25,26 | 24.67 | |
Parallel | 34,27,27 | 29.33 | ||
ForkJoinPool | 27,26,28 | 27.00 | ||
yield | 114,98,107 | 106.33 | ||
Constructor | ThreadPool | 24,25,24 | 24.33 | |
Parallel | 70,66,27 | 54.33 | ||
ForkJoinPool | 42,30,26 | 32.67 | ||
yield | 116,112,108 | 112.00 |
グラフは、各平均値の棒グラフ
ソースの話(目デバック用)
インスタンス生成を多く発生させる目的で、計算結果を表すものをLombokで定義
import lombok.ToString;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Data {
// 何番目
private long nth;
// Fn-1
private long n1;
// Fn-2
private long n2;
// Fn
long num() { return n1 + n2; }
}
実行用クラス、ParallelStream,ThreadPool,yield周りは最適化が必要な状態
@Functionalinterface
interface Constructor {
Data instance(long nth, long n1, long n2);
}
public class App {
public static void main(String[] args) {
System.out.println("[Start] java version::" + System.getProperty("java.version"));
//Map<String,Constructor> cm = new HashMap<String,Constructor>(){{
Map<String,Constructor> cm = new HashMap<>(){{
// Builder でインスタンス生成
put("Builder", (nth, n1, n2) -> {
return Data.builder()
.nth(nth).n1(n1).n2(n2).build();
});
// Constructor でインスタンス生成
put("Constructor", (nth, n1, n2) -> {
return new Data(nth, n1, n2);
});
// Setter でインスタンス生成
put("Setter", (nth, n1, n2) -> {
Data d = new Data();
d.setNth(nth);
d.setN1(n1);
d.setN2(n2);
return d;
});
}};
for (Map.Entry<String,Constructor> ct : cm.entrySet()) {
LongStream.range(1, 4).forEach(n -> {check(n, ct);});
}
}
static void check(long n, Map.Entry<String,Constructor> ct) {
final App app = new App(ct.getValue());
//List<Function<LongStream,List<Data>>> logics = new ArrayList<Function<LongStream,List<Data>>>(){{
// Parallel, ForkJoin, yield, ThreadPool の4ケースの処理
List<Function<LongStream,List<Data>>> logics = new ArrayList<>(){{
add(app::runStream);
add(app::runForkJoinStream);
add(app::runYield);
add(app::runThreadPool);
}};
System.out.println("[" + n + "] <" + ct.getKey() + ">");
logics.forEach(l -> app.calc(l));
}
Constructor cs;
App(Constructor c) {
this.cs = c;
}
// 1回実行
void calc(Function<LongStream,List<Data>> logic) {
long start = System.currentTimeMillis();
List<Data> datas = logic.apply(LongStream.range(0, 30));
long end = System.currentTimeMillis();
// 全結果表示 String result = datas.stream().map(d -> { return d.getNth() + "=" + d.num(); }).collect(Collectors.joining(","));
String result = datas.get(datas.size()-1).toString();
System.out.println(" exe time:" + (end - start) + " mill sec [" + result + "]");
}
// 検証したいところ
Data fib(long n) {
if (n == 0) {
return cs.instance(n, 0, 0);
} else if (n == 1) {
return cs.instance(n, n, 0);
}
return cs.instance(n, fib(n-1).num(), fib(n-2).num());
}
// for yield
class Fib implements Callable<Data> {
private long n;
private long counter;
Fib(long n) {
this.counter = 0;
this.n = n;
}
Data fib(long n) {
this.counter++;
if (this.counter % 8 == 0) {
Thread.yield();
}
if (n == 0) {
return cs.instance(n, 0, 0);
} else if (n == 1) {
return cs.instance(n, n, 0);
}
return cs.instance(n, fib(n-1).num(), fib(n-2).num());
}
public Data call() {
return fib(this.n);
}
}
List<Data> runStream(LongStream st) {
System.out.print("Parallel>");
return st.parallel().mapToObj(this::fib).collect(Collectors.toList());
}
List<Data> runForkJoinStream(LongStream st) {
System.out.print("ForkJoin>");
try { return new ForkJoinPool(4).submit(() -> {return runStream(st);}).get(); }
catch (Throwable e) { return null; }
}
List<Data> runThreadPool(LongStream st) {
return runThreadPool("Thradpool>", st, 4, (n) -> { return () -> fib(n); });
}
List<Data> runYield(LongStream st) {
return runThreadPool("Yield>", st, 3, (n) -> { return new Fib(n); });
}
List<Data> runThreadPool(String name, LongStream st, int size, LongFunction<Callable<Data>> map) {
System.out.print(name);
List<Callable<Data>> tasks = st.mapToObj(map).collect(Collectors.toList());
ExecutorService exe = Executors.newFixedThreadPool(size);
try {
List<Future<Data>> result = exe.invokeAll(tasks);
return result.stream().map(this::conv).collect(Collectors.toList());
} catch (Throwable e) {
e.printStackTrace();
return null;
} finally {
exe.shutdown();
}
}
Data conv(Future<Data> f) {
try {return f.get();} catch (Exception e) {return null;}
}
}
余談
lombok と Java9
java9 は未対応なので、ソースを生成して実行するしかいない。
アノテーションは使えない。
$ java -jar lombok.jar delombok -f pretty -p src/main/lombok/Data.java
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by lombok.javac.JavacTreeMaker$TypeTag to method com.sun.tools.javac.code.Type.getTag()
WARNING: Please consider reporting this to the maintainers of lombok.javac.JavacTreeMaker$TypeTag
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
Exception in thread "main" java.lang.NoSuchFieldError: text
at lombok.javac.Javac8BasedLombokOptions.putJavacOption(Javac8BasedLombokOptions.java:42)
at lombok.delombok.Delombok.delombok(Delombok.java:472)
at lombok.delombok.Delombok.main(Delombok.java:256)
at lombok.delombok.DelombokApp.runDirectly(DelombokApp.java:176)
at lombok.delombok.DelombokApp.runApp(DelombokApp.java:47)
at lombok.core.Main.go(Main.java:128)
at lombok.core.Main.main(Main.java:43)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at lombok.launch.Main.main(Main.java:36)
Java8 と 匿名クラス
Java8 では匿名内部クラスの型推論出来ない。
エラー: HashMap<K,V>の型引数を推論できません
Map<String,Constructor> cm = new HashMap<>(){{
^
理由: 匿名内部クラスでは'<>'を使用できません
K,Vが型変数の場合:
クラス HashMapで宣言されているKはObjectを拡張します
クラス HashMapで宣言されているVはObjectを拡張します
エラー: ArrayList<E>の型引数を推論できません
List<Function<LongStream,List<Data>>> logics = new ArrayList<>(){{
^
理由: 匿名内部クラスでは'<>'を使用できません
Eが型変数の場合:
クラス ArrayListで宣言されているEはObjectを拡張します