lombok のインスタンス生成周りの検証にゃ


## これは

* lombok　使うと便利なので、実際のインスタンス生成周りのパフォーマンスを調べてみた
* 結果としては、Setter感でした

※ Javaについては、7まで嫌いだったが8から普通な感じ

## 検査内容

jvm version 8,9 (2つ) x builder/コンストラクタ/setter（３つ）
x ThreadPool, ForkJoinPool, ParallelStream, yield (４つ) の計２４パターン
について、fib計算をLongStream.range(0,30)を実行してインスタンス生成の時間計測

## 計測結果

８の方が処理速度早めで、Setterが最速で ThreadPool が早そう

<table>
<tr>
<th>jvm</th>
<th>インスタンス作成</th>
<th>実行パターン</th>
<th>時間(mill sec) 3回計測</th>
<th>Avg</th>
</tr>

<tr><td rowspan="12">8</td><td rowspan="4">Builder</td>
<td>ThreadPool</td><td>19,21,20</td><td>20.00</td>
</tr>

<tr><td>Parallel</td><td>54,20,21</td><td>31.67</td></tr>
<tr><td>ForkJoinPool</td><td>94,31,20</td><td>48.33</td></tr>
<tr><td>yield</td><td>288,92,103</td><td>161</td></tr>

<tr><td rowspan="4">Setter</td>
<td>ThreadPool</td><td>24,21,23</td><td>22.67</td></tr>
<tr><td>Parallel</td><td>32,25,21</td><td>26.00</td></tr>
<tr><td>ForkJoinPool</td><td>25,24,24</td><td>24.33</td></tr>
<tr><td>yield</td><td>104,93,94</td><td>97.00</td></tr>

<tr><td rowspan="4">Constructor</td>
<td>ThreadPool</td><td>34,38,24</td><td>32.00</td></tr>
<tr><td>Parallel</td><td>72,46,52</td><td>56.67</td></tr>
<tr><td>ForkJoinPool</td><td>38,31,21</td><td>30.00</td></tr>
<tr><td>yield</td><td>114,84,88</td><td>95.33</td></tr>

<tr><td rowspan="12">9</td><td rowspan="4">Builder</td>
<td>ThreadPool</td><td>50,24,24</td><td>32.67</td>
</tr>

<tr><td>Parallel</td><td>80,29,29</td><td>46.00</td></tr>
<tr><td>ForkJoinPool</td><td>23,27,26</td><td>25.33</td></tr>
<tr><td>yield</td><td>126,103,98</td><td>109.00</td></tr>

<tr><td rowspan="4">Setter</td>
<td>ThreadPool</td><td>23,25,26</td><td>24.67</td></tr>
<tr><td>Parallel</td><td>34,27,27</td><td>29.33</td></tr>
<tr><td>ForkJoinPool</td><td>27,26,28</td><td>27.00</td></tr>
<tr><td>yield</td><td>114,98,107</td><td>106.33</td></tr>

<tr><td rowspan="4">Constructor</td>
<td>ThreadPool</td><td>24,25,24</td><td>24.33</td></tr>
<tr><td>Parallel</td><td>70,66,27</td><td>54.33</td></tr>
<tr><td>ForkJoinPool</td><td>42,30,26</td><td>32.67</td></tr>
<tr><td>yield</td><td>116,112,108</td><td>112.00</td></tr>

</table>

グラフは、各平均値の棒グラフ

![jlombok.png](https://qiita-image-store.s3.amazonaws.com/0/106271/3f7f75bc-481e-7775-bc32-80b64ff74a00.png)


## ソースの話（目デバック用）

インスタンス生成を多く発生させる目的で、計算結果を表すものをLombokで定義

```java
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周りは最適化が必要な状態

```java

@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 の４ケースの処理
        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;
    }

    // １回実行
    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 は未対応なので、ソースを生成して実行するしかいない。
アノテーションは使えない。

```shell

$ 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を拡張します
```



## Reference

* [ForkJoinPool parallelStreamの制御用](https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/ForkJoinPool.html)

* [Lombok Java9まだよの話](https://projectlombok.org/setup/javac)