Java 奇怪的优化器行为:JVM坚持一种优化,不';不适应
我有一个漫长的过程,主要有两个阶段。第一阶段和第二阶段的执行路径略有不同 我刚刚意识到,根据JMH(Java 奇怪的优化器行为:JVM坚持一种优化,不';不适应,java,jvm,openjdk,jmh,Java,Jvm,Openjdk,Jmh,我有一个漫长的过程,主要有两个阶段。第一阶段和第二阶段的执行路径略有不同 我刚刚意识到,根据JMH(-server,onjava-7-openjdk-amd64)的说法,如果我用不同的名称创建同一方法的副本,并在每个阶段使用不同的名称,那么在第二阶段中,我的方法调用的加速率将超过25%(在5次调用预热后,对该方法的调用超过数百万次,以5次调用衡量) 有没有办法告诉JVM忘记以前对方法的优化,从头开始重新学习? 在下面的示例代码中,基准方法是run,在stage2中调用checkChar和chec
-server
,onjava-7-openjdk-amd64
)的说法,如果我用不同的名称创建同一方法的副本,并在每个阶段使用不同的名称,那么在第二阶段中,我的方法调用的加速率将超过25%(在5次调用预热后,对该方法的调用超过数百万次,以5次调用衡量)
有没有办法告诉JVM忘记以前对方法的优化,从头开始重新学习?
在下面的示例代码中,基准方法是run
,在stage2
中调用checkChar
和checkChar0
的两个版本之间进行比较
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 {
...
}
}
final public void run(){
sumStg1=0;
对于(int i=0;i<10000;i++){
String str=consumerString();
对于(int i=0;i我想到了两个想法:
A) 通过调用并使用指向同一方法的新方法句柄更新该方法,然后调用应触发调用方法的重新编译
A提供了类似的功能
我不知道JVM是否会在这里应用任何技巧,如果方法被替换为它自己,就跳过重新编译
B) 应用一个本质上不会改变任何东西的类文件,除了可能向方法体附加一些惰性字节码或其他什么
请注意,我还没有尝试过这些,所以不能保证
还有一种可能是,您没有对您打算进行基准测试的内容进行基准测试。JMH并不是通过在代码中挥动它来消除所有测量问题的魔棒
您说您的实际应用程序由两个阶段组成,并且有一个阶段转换
但在JMH中,您总共使用了10次迭代(每次调用N次),这意味着在新迭代开始时,将有10*N次前向阶段转换和10*N次后向转换。这将最终导致JIT放弃重新编译
JMH旨在测量稳态性能,而您的应用程序依赖于一次性行为。另外两个想法:
- 在新的类加载器中再次加载该类,并使用该版本而不是静态加载的版本
- 为
-XX:CompileThreshold
设置更大的值
(这两种方法都有显著/明显的缺点,但如果性能对您如此重要……)这两个选项看起来都很有趣。然而,这种粘性优化器行为是否违背了面向对象的核心原则?子类以不同的方式使用继承的方法是非常合理的,并且优化将针对在程序/服务的开始阶段最常使用它的子类。Am我在这里遗漏了什么?不是全局使用了哪些(子)类(尽管类层次结构分析也有影响),而是调用站点使用了哪些类(假设代码已内联到该调用站点)。因此,如果您在特定的调用站点更改类型配置文件,这对优化器来说是一个问题。它会尝试适应几次,但最终放弃不浪费CPU周期的反复优化。也许您问错了问题…单独的类加载器听起来确实像是一种实用的方法。它让我思考我是否我们会使用匿名类来实现同样的效果。不……那没有帮助。匿名类会被加载……并进行优化……就像一个普通的命名类一样。“匿名”仅仅意味着你不能在Java源代码中命名该类。