我々は、次のコードを持っていると仮定しましょう:
public static void check() {
if (Config.initialized) {
...
}
}
Config.initializedは、初めに偽であり、この方法は、JITすでにコンパイルされた後にのみ、いくつかの点でtrueに変更します。値がfalseに戻ったことがありません。
私は非常に洗練された継続的な最適化(ループアンローリング、分岐予測、インライン化、エスケープ解析など)がたくさんある「知っている」と私は詳細にそれらのすべてを理解することから遠く離れたんだが、私は主に次のように興味今のところ:
JITコンパイラは、チェックが完全にスキップすることができるように、常に、ある時点の後に真になる場合ことを検出する方法がありますか?完全にすることで、私は本当に何の変数へのアクセス、無条件チェック/ JNE、などを意味していません...
ケースでJITのための方法は、サンプルから(ある時点から以降)の不要なチェックを取り除くことはありません、私はそれをサポートするために行うことができますが何である(と私はそれができるか分からないでしょう)を?私の唯一のアイデアは、クラスを再変換するために、初期化イベントが発生した後に、バイトコードから不要なコードを削除することです。
私は、これは、総マイクロ最適化され、おそらくハードさえJMHのようなツールを使用してmeassureすることを承知していますが、私はまだのように知っていると理解することでしょう。
最後だが大事なことは:
- で、私の上記の方法は、これらすべてのメソッドが再コンパイルされることをどこかにインライン展開しまった場合のように変化した場合の何かに(と仮定すると、彼らは熱い)ことを正しく理解
check
する方法の必要性を再コンパイルするには?
私は私のJitWatchテストの結果を理解していれば正常に上記の質問への答えは次のようになります。
- いや、決して。常に条件チェックがあります。
- 実際には変換による
- はい
- JITコンパイラは、以下の場合には、常に特定のポイントの後に真となることを検出する方法を持っています
フィールドがある場合は、[はい、static final
とそのホルダークラスは時間JITコンパイラキックで初期化されている。どうやらこれがあるため、あなたのケースでは適用されないConfig.initialized
行うことはできませんstatic final
。
- 私はそれをサポートするために何ができるものはありますか?
java.lang.invoke.MutableCallSite
救助へ。
このクラスは、あなたが求めることをやってのために特別に設計されています。そのsetTarget
ランタイムで呼び出しサイトを再バインドする方法がサポート。ボンネットの下には、新しいターゲットと後でそれを再コンパイルする可能性が現在コンパイルされたメソッドの脱最適化の原因となります。
A MethodHandle
呼び出すためのMutableCallSite
ターゲットを用いて得ることができるdynamicInvoker
方法。注意MethodHandle
する必要がありますstatic final
インライン化できるようにするために。
- 上記の方法は、これらすべてのメソッドが再コンパイルされることをインラインどこを得た場合
はい。
ここではそれを実証するベンチマークであるmutableCallSite
方法は、の速さであるalwaysFalse
ほど高速でも冒頭に、とalwaysTrue
トグルを切り替えた後。@Holgerが示唆したように、私はまた、比較のために、静磁場トグルが含まれています。
package bench;
import org.openjdk.jmh.annotations.*;
import java.lang.invoke.*;
import java.util.concurrent.*;
@State(Scope.Benchmark)
public class Toggle {
static boolean toggleField = false;
static final MutableCallSite toggleCallSite =
new MutableCallSite(MethodHandles.constant(boolean.class, false));
static final MethodHandle toggleMH = toggleCallSite.dynamicInvoker();
public void switchToggle() {
toggleField = true;
toggleCallSite.setTarget(MethodHandles.constant(boolean.class, true));
MutableCallSite.syncAll(new MutableCallSite[]{toggleCallSite});
System.out.print("*** Toggle switched *** ");
}
@Setup
public void init() {
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
executor.schedule(this::switchToggle, 10100, TimeUnit.MILLISECONDS);
executor.shutdown();
}
@Benchmark
public int alwaysFalse() {
return 0;
}
@Benchmark
public int alwaysTrue() {
return ThreadLocalRandom.current().nextInt();
}
@Benchmark
public int field() {
if (toggleField) {
return ThreadLocalRandom.current().nextInt();
} else {
return 0;
}
}
@Benchmark
public int mutableCallSite() throws Throwable {
if ((boolean) toggleMH.invokeExact()) {
return ThreadLocalRandom.current().nextInt();
} else {
return 0;
}
}
}
5回のウォームアップの繰り返しと、私は次のような結果を得る10回の測定の反復とのベンチマークを実行します:
# JMH version: 1.20
# VM version: JDK 1.8.0_192, VM 25.192-b12
# Benchmark: bench.Toggle.alwaysFalse
# Run progress: 0,00% complete, ETA 00:01:00
# Fork: 1 of 1
# Warmup Iteration 1: 3,875 ns/op
# Warmup Iteration 2: 3,369 ns/op
# Warmup Iteration 3: 2,699 ns/op
# Warmup Iteration 4: 2,696 ns/op
# Warmup Iteration 5: 2,703 ns/op
Iteration 1: 2,697 ns/op
Iteration 2: 2,696 ns/op
Iteration 3: 2,696 ns/op
Iteration 4: 2,706 ns/op
Iteration 5: *** Toggle switched *** 2,698 ns/op
Iteration 6: 2,698 ns/op
Iteration 7: 2,692 ns/op
Iteration 8: 2,707 ns/op
Iteration 9: 2,712 ns/op
Iteration 10: 2,702 ns/op
# Benchmark: bench.Toggle.alwaysTrue
# Run progress: 25,00% complete, ETA 00:00:48
# Fork: 1 of 1
# Warmup Iteration 1: 5,159 ns/op
# Warmup Iteration 2: 5,198 ns/op
# Warmup Iteration 3: 4,314 ns/op
# Warmup Iteration 4: 4,321 ns/op
# Warmup Iteration 5: 4,306 ns/op
Iteration 1: 4,306 ns/op
Iteration 2: 4,310 ns/op
Iteration 3: 4,297 ns/op
Iteration 4: 4,324 ns/op
Iteration 5: *** Toggle switched *** 4,356 ns/op
Iteration 6: 4,300 ns/op
Iteration 7: 4,310 ns/op
Iteration 8: 4,290 ns/op
Iteration 9: 4,297 ns/op
Iteration 10: 4,294 ns/op
# Benchmark: bench.Toggle.field
# Run progress: 50,00% complete, ETA 00:00:32
# Fork: 1 of 1
# Warmup Iteration 1: 3,596 ns/op
# Warmup Iteration 2: 3,429 ns/op
# Warmup Iteration 3: 2,973 ns/op
# Warmup Iteration 4: 2,937 ns/op
# Warmup Iteration 5: 2,934 ns/op
Iteration 1: 2,927 ns/op
Iteration 2: 2,928 ns/op
Iteration 3: 2,932 ns/op
Iteration 4: 2,929 ns/op
Iteration 5: *** Toggle switched *** 3,002 ns/op
Iteration 6: 4,887 ns/op
Iteration 7: 4,866 ns/op
Iteration 8: 4,877 ns/op
Iteration 9: 4,867 ns/op
Iteration 10: 4,877 ns/op
# Benchmark: bench.Toggle.mutableCallSite
# Run progress: 75,00% complete, ETA 00:00:16
# Fork: 1 of 1
# Warmup Iteration 1: 3,474 ns/op
# Warmup Iteration 2: 3,332 ns/op
# Warmup Iteration 3: 2,750 ns/op
# Warmup Iteration 4: 2,701 ns/op
# Warmup Iteration 5: 2,701 ns/op
Iteration 1: 2,697 ns/op
Iteration 2: 2,696 ns/op
Iteration 3: 2,699 ns/op
Iteration 4: 2,706 ns/op
Iteration 5: *** Toggle switched *** 2,771 ns/op
Iteration 6: 4,310 ns/op
Iteration 7: 4,306 ns/op
Iteration 8: 4,312 ns/op
Iteration 9: 4,317 ns/op
Iteration 10: 4,301 ns/op