今日も Valhalla を少しだけゴニョゴニョします。
Valhalla はプロジェクトがそんなに進んでなく、試せることがほとんどありません。
そんな中で Generic Specialization を使った GenericMethods を試してみます。
これもネット上で見かけるコードを試してみたりしたのですが動かないものが多いですね。
あまり良いサンプルではないけどブログ更新のお遊びとしていちおう動いたコードで未来の技術のパフォーマンスを少しでも感じ取ってみます。
それでは下記のプログラムをご覧ください。
package genericMethods;
import java.util.function.Supplier;
public class GenericMethods {
public static T hoge(Supplier supplier) {
return supplier.get();
}
public static T piyo(T i) {
return i;
}
public static void main(String[] args) {
int n = 7;
System.out.println(hoge(() -> n) + " : " + hoge(() -> n).getClass());
System.out.println(piyo(n) + " : " + piyo(n).getClass());
}
}
これは int 型の引数 7 を渡してそのまま返してもらうだけのプログラムです。
java.util.function.Supplier を使ったものと普通のコード二つを実行しています。
プログラムの実行結果は次のようになります。
Integer 型になって返ってきました。
コストの高い Autoboxing が働いてしまいました。
これを Valhalla の Generic Specialization を使えば Autoboxing によるパフォーマンスの低下を回避できるかもしれません。
さっそく試してみました。
しかし、java.util.function.Supplier に Generic Specialization を使おうとしたけど駄目でした。(>_<。)
欲張らずに普通ので我慢することに・・・(^_^;)
そういうことで下記のようなプログラムを組んで Valhalla の Generic Specialization の実力とやらを見せてもらいました。
public class GenericMethods2 {
private static long start;
private static long time;
// 古いジェネリクス
public static T hoge(T i) {
return i;
}
// ジェネリクス スペシャライゼーション
public static T piyo(T i) {
return i;
}
public static void main(String[] args) {
int a = 7;
System.out.println(hoge(a).getClass() + " : " + hoge(a)); // class java.lang.Integer : 7
// System.out.println(piyo(a).getClass() + " : " + piyo(a)); エラー: intは間接参照できません
System.out.println(piyo(a)); // 7
// ウォーミングアップ
for (int i = 0; i < 20; i++) {
calcInteger();
}
for (int i = 0; i < 20; i++) {
calcInt();
}
// 処理速度計測 古いジェネリク
start = System.nanoTime();
calcInteger();
time = System.nanoTime() - start;
System.out.println(System.lineSeparator() + "(○・ω・)ノ------------- 古いジェネリクス "
+ (int) (time * 1e-9) / 3_600 + "時間"
+ (int) ((time * 1e-9) / 60) % 60 + "分"
+ (int) (time * 1e-9 % 60) + "秒"
+ Double.toString((time * 1e-9 % 60) % 1).substring(2) + " ------------ )" + System.lineSeparator());
// 処理速度計測 ジェネリクス スペシャライゼーション
start = System.nanoTime();
calcInt();
time = System.nanoTime() - start;
System.out.println(System.lineSeparator() + "(○・ω・)ノ------------- ジェネリクス スペシャライゼーション "
+ (int) (time * 1e-9) / 3_600 + "時間"
+ (int) ((time * 1e-9) / 60) % 60 + "分"
+ (int) (time * 1e-9 % 60) + "秒"
+ Double.toString((time * 1e-9 % 60) % 1).substring(2) + " ------------ )" + System.lineSeparator());
System.out.println();
System.out.println("お終い!");
}
private static void calcInteger() {
for (int i = 0; i < 7; i++) {
int n = 0;
for (int j = 0; j < 2100_000_000; j++) {
n = hoge(j);
// System.out.println(n);
}
}
}
private static void calcInt() {
for (int i = 0; i < 7; i++) {
int n = 0;
for (int j = 0; j < 2100_000_000; j++) {
n = piyo(j);
// System.out.println(n);
}
}
}
}
このプログラムを動かすと Generic Specialization を利用したメソッドの返り値は int 型となってます。
これで Autoboxing の呪いから解放されるぜ!
処理速度がどれだけ違うかプログラムの実行結果をご覧ください。
笑っちゃうくらい大きな差がありますね。
Java にジェネリクスを導入するときにプリミティブ型の対応をしなかったツケが浮き彫りになってます。
Autoboxing は便利だけど私は知らず知らずのうちに働かせてしまうので早く Valhalla が使える日がくるように祈ってます。