2

我有一个漫长的过程,有两个主要阶段。第一阶段和第二阶段的执行路径略有不同。

我刚刚意识到,如果我创建具有不同名称的相同方法的副本并在每个阶段使用不同的名称,根据 JMH ( -server, on java-7-openjdk-amd64),我在第二阶段的方法调用中获得超过 25% 的加速(在 5 次调用预热后,对该方法的数百万次调用,以 5 次调用来衡量)。

有没有办法告诉 JVM 忘记以前对方法的优化并从头开始重新学习?

在下面的示例代码中,基准测试方法是,并且在调用和in 的run两个版本之间进行比较。checkCharcheckChar0stage2

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 {
     ...
   }
}
4

2 回答 2

0

想到两个想法:

A) 通过可变的 CallSite调用该方法,并使用指向同一方法的新方法句柄对其进行更新,然后调用syncAll()应该会触发调用方法的重新编译。

SwitchPoint提供类似的功能。

我不知道 JVM 是否可以在这里应用任何聪明之处,如果方法被自己替换,则跳过重新编译。

B)应用一个基本上不会改变任何东西的类文件重新转换,除了可能将一些惰性字节码附加到方法体或其他任何东西。

请注意,我没有尝试过任何这些,所以不能保证。


您也有可能没有对您打算进行基准测试的内容进行基准测试。JMH 并不是通过在代码上挥舞它来解决所有测量问题的魔术棒。

您说您的实际应用程序由两个阶段组成,并且只有一个阶段转换。

但是在 JMH 中,您总共使用了 10 次迭代(每次有 N 次调用),这意味着新迭代开始时有 10*N 前向相变和 10*N 后向过渡。这最终会导致 JIT 放弃重新编译。

JMH 旨在测量稳态性能,而您的应用程序依赖于一次性行为。

于 2015-07-26T06:22:19.993 回答
0

另外几个想法:

  • 在新的类加载器中再次加载该类并使用该版本而不是静态加载的版本。

  • 为 设置一个更大的值-XX:CompileThreshold

(这两者都有显着/明显的缺点,但如果性能对你来说很重要......)

于 2015-07-26T06:26:58.690 回答