jep280: java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

23
Java 9 文字列結合の 処理が変わるぞ! 準備はいいか!? @YujiSoftware

Upload: yujisoftware

Post on 21-Jan-2018

1.897 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

Java 9で

文字列結合の処理が変わるぞ!

準備はいいか!?

@YujiSoftware

Page 2: JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

問題

• +演算子による文字列結合は最終的にどのような処理になる?

private static String test(String str, int value) {

return "ABC” + str + value;}

Page 3: JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

Java 8までは

• コンパイル時に StringBuilderを使った処理になる

private static String test(String str, int value) {

return new StringBuilder()

.append("ABC")

.append(str)

.append(value)

.toString();}

Page 4: JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

Java 9では

• InvokeDynamicで、実行時に処理を作る

private static String test(String str, int value) {

return InvokeDynamic#1:makeConcatWithConstants:

(II)Ljava/lang/String;}

Page 5: JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

そして…

Page 6: JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

InvokeDynamicの結果!

• 「byte配列を生成し、そこに文字を詰める処理」が出来上がる!

private static String test(String str, int value) {

int length = (文字列の長さを計算);

byte[] buf = new byte[length];

int index = buf.length;

index = StringConcatHelper.prepend(index, buf, coder, value);

index = StringConcatHelper.prepend(index, buf, coder, str);

index = StringConcatHelper.prepend(index, buf, coder, "ABC");

return new String(buf,coder);}

Page 7: JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

+演算子による文字列結合で

StringBuilderは使わなくなった!

Page 8: JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

どういうこと?

• JEP280 (Indy String Concatenation)の対応

–再コンパイルなしで、文字列結合を最適な処理に変更できるようにする

InvokeDynamicを使って、実行時に処理を作るように変更する

–そのついでに、より最適化された処理を作るようにしよう!(ストレッチゴール)

いくつかの案を試すことになった

Page 9: JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

実装された案案(ストラテジー) 概要

BC_SB StringBuilderを使ったバイトコードを生成する(既存と同様)

BC_SB_SIZED StringBuilderを使ったバイトコードを生成する。加えて、必要な配列のサイズを「推定」する。

BC_SB_SIZED_EXACT StringBuilderを使ったバイトコードを生成する。加えて、必要な配列のサイズを「正確に計算」する。

MH_SB_SIZED MethodHandleベースのジェネレータを使って、最終的にStringBuilderを呼び出す。加えて、必要な配列のサイズを「推定」する。

MH_SB_SIZED_EXACT MethodHandleベースのジェネレータを使って、最終的にStringBuilderを呼び出す。加えて、必要な配列のサイズを「正確に計算」する。

MH_INLINE_SIZED_EXACT MethodHandleベースのジェネレータを使って、独自にbyte配列を構築する。必要な配列のサイズを「正確に計算」する。

※起動オプション –D:java.lang.invoke.stringConcat=(ストラテジー名) で変更可能

Page 10: JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

採用された案

• MH_INLINE_SIZED_EXACTが採用された

–唯一、StringBuilderを使わない実装

• 特徴

–各APIをMethodHandleでコールする処理を作る

–文字列を格納する配列の長さを、最初に厳密に計算する

• 足りなくなったら新しく配列を作り直す、というStringBuilderの無駄を避ける

Page 11: JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

文字列結合の処理はどうやって作られるのか

Page 12: JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

java.lang.invoke.StringConcatFactory#makeConcatWithConstantsの実装

// Start building the combinator tree. The tree "starts" with (<parameters>)String, and "finishes"

// with the (int, byte[], byte)String in String helper. The combinators are assembled bottom-up,

// which makes the code arguably hard to read.

// Drop all remaining parameter types, leave only helper arguments:

MethodHandle mh;

mh = MethodHandles.dropArguments(NEW_STRING, 3, ptypes);

// Mix in prependers. This happens when (byte[], int, byte) = (storage, index, coder) is already

// known from the combinators below. We are assembling the string backwards, so "index" is the

// *ending* index.

for (RecipeElement el : recipe.getElements()) {

// Do the prepend, and put "new" index at index 1

mh = MethodHandles.dropArguments(mh, 2, int.class);

switch (el.getTag()) {

case TAG_CONST: {

Object cnst = el.getValue();

MethodHandle prepender = MethodHandles.insertArguments(prepender(cnst.getClass()), 3, cnst);

mh = MethodHandles.foldArguments(mh, 1, prepender,

2, 0, 3 // index, storage, coder

);

break;

}

case TAG_ARG: {

int pos = el.getArgPos();

MethodHandle prepender = prepender(ptypes[pos]);

mh = MethodHandles.foldArguments(mh, 1, prepender,

2, 0, 3, // index, storage, coder

4 + pos // selected argument

);

break;

}

Page 13: JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

さっぱり分からん

(´・ω・`)

Page 14: JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

作戦変更

• JIT Watch で、makeConcatWithConstants

メソッドにより生成された処理を確認

• JIT Watchとは?

– ソースコードと、それをコンパイルしたバイトコード、さらに実行時のアセンブラを並べて比較できるツール

– どのような処理が実行されたのかがひと目でわかる

Page 15: JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

生成された処理0x000001df3b29f540: cmp $0xfa0a1f00,%r10d0x000001df3b29f547: jg L002f0x000001df3b29f54d: cmp $0xc4653600,%r10d0x000001df3b29f554: jle L0050 ;*if_icmple {reexecute=0 rethrow=0 return_oop=0}

; - java.lang.Integer::stringSize@24 (line 542); - java.lang.StringConcatHelper::mixLen@2 (line 96); - java.lang.invoke.DirectMethodHandle$Holder::invokeStatic@11; - java.lang.invoke.LambdaForm$BMH/400136488::reinvoke@48; - java.lang.invoke.LambdaForm$MH/1879034789::linkToTargetMethod@6; - Test::test@17 (line 15)

0x000001df3b29f55a: mov $0x8,%ecx0x000001df3b29f55f: jmp L0003

L0002: mov $0x2,%ecxL0003: inc %ecx ;*iinc {reexecute=0 rethrow=0 return_oop=0}

; - java.lang.Integer::stringSize@36 (line 541); - java.lang.StringConcatHelper::mixLen@2 (line 96); - java.lang.invoke.DirectMethodHandle$Holder::invokeStatic@11; - java.lang.invoke.LambdaForm$BMH/400136488::reinvoke@48; - java.lang.invoke.LambdaForm$MH/1879034789::linkToTargetMethod@6; - Test::test@17 (line 15)

L0004: mov %ecx,%r8d0x000001df3b29f56b: add $0x5,%r8d ;*iadd {reexecute=0 rethrow=0 return_oop=0}

; - java.lang.StringConcatHelper::mixLen@5 (line 96)

Page 16: JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

時間がないので流れだけ説明すると…

Page 17: JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

Phase 1. byte配列を作る

1. 結合する各文字列の長さを計算する

2. 合計分の長さのbyte配列を作成する

byte[] buf = new byte[10]

例:“ABC” → 31357 → 5obj → obj.toString().length() → 3

Page 18: JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

Phase 2. byte配列に文字列を詰める

3. 末尾の方から、byte配列に文字を詰めていく

–末尾から処理する理由は不明

• メモリのアクセス効率がいい?

A B C 1 3 5 7 o b j

“A B C”1 3 5 7

o b j

byte配列

Page 19: JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

Phase 3.文字列にする

4. 作った配列を Stringにする

new String(buf, coder);

A B C 1 3 5 7 o b j

完成!

Page 20: JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

どのぐらい速くなったか

• 文字列にもよるが、1倍~3倍程度速くなる

• ただし、最初だけ少し遅い

– InvokeDynamicで処理を作る必要があるため

• 詳しくは、JEP280 の資料を参照

– http://cr.openjdk.java.net/~shade/8085796/notes.txt

Page 21: JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

まとめ

• Java 9で + 演算による文字列結合の処理が変わった

– StringBuilderが使われなくなった

–パフォーマンスも向上した

• Java 9 を使う理由がまた一つ増えた!

• ぜひアップデートした知識を使って、Javaをアップデートしましょう!

Page 22: JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

Java 9で

文字列結合の処理が変わるぞ!

準備はいいか!?

@YujiSoftware

Page 23: JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc

ちなみに

• 後日、詳細をブログにまとめる予定

•地平線に行くで検索– http://d.hatena.ne.jp/chiheisen/