我有一个漫长的过程,有两个主要阶段。第一阶段和第二阶段的执行路径略有不同。
我刚刚意识到如果我使用不同的名称创建相同方法的副本并在每个阶段使用不同的名称,根据JMH(-server
,java-7-openjdk-amd64
),我得到的不仅仅是在第二阶段调用方法调用的速度提高25%(在调用5次调用后,通过5次调用对方法进行数百万次调用)。
有没有办法告诉JVM忘记以前关于方法的优化并从头开始重新学习?
在以下示例代码中,基准化方法为run
,并且在checkChar
中调用checkChar0
和stage2
的两个版本之间进行了比较。
final public void run(){
sumStg1=0;
for(int i=0; i< 10000; i++){
String str = consumeString();
for(int i= 0; i<K; i++){
sumStg1 += checkChar(str.charAt(i), i)?1:0;
}
}
sumStg2=0;
for(int j=0; j< 10000000; j++){
String str = consumeString();
for(int i=K/2; i<str.length(); i++){
sumStg2 += checkChar(str.charAt(i), i)?1:0;
}
}
}
final public boolean checkChar(char in, int i){
if(i < K/2){
...
} else if (i < K){
...
} else {
...
}
}
//identical method to checkChar
final public boolean checkChar0(char in, int i){
if(i < K/2){
...
} else if (i < K){
...
} else {
...
}
}
答案 0 :(得分:0)
我想到了两个想法:
A)通过mutable CallSite调用该方法,并使用指向同一方法的新方法句柄更新它,然后调用syncAll()应该触发重新编译调用方法。
SwitchPoint提供了类似的功能。
我不知道JVM是否可以在这里应用任何聪明,如果方法被自身替换,则跳过重新编译。
B)应用一个基本上不会改变任何东西的类文件retransformation,除了可能会向方法体附加一些惰性字节码或其他任何东西。
请注意,我没有尝试任何此类操作,因此无法保证。
您也可能没有对您打算进行基准测试的情况进行基准测试。 JMH不是通过在代码中挥动它来使所有测量问题消失的魔术棒。
您说您的实际应用程序包含两个阶段,并且只有一个阶段转换。
但是在JMH中,你总共使用了10次迭代(每次都有N次调用),这意味着当新的迭代开始时,10 * N正向相位转换和10 * N向后转换。这最终会导致JIT放弃重新编译。
JMH旨在测量稳态性能,而您的应用程序依赖于一次性行为。
答案 1 :(得分:0)
另外几个想法:
在新的类加载器中再次加载该类,并使用该版本而不是静态加载的版本。
为-XX:CompileThreshold
设置更大的值。
(这些都有明显/明显的缺点,但如果性能对你很重要......)