- Имеет ли JIT-компилятор способ определить, что if всегда будет истинным после определенной точки
Да, если поле равно 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