为什么这里Java方法调用会如此昂贵?

8

我有一个线程执行器实现,其中包装器的成本非常高。有一个被定义如下的包装器类Task:

 class Task {
   public Runnable r;       
   public Task(Runnable r) {          
     this.r = r;                                                                   
   }  

   public void run() {       
     r.run();      
   }     

 List<task> taskList;

以下情况的运行时间为约800毫秒。
  for (Task t : taskList) {                                                 
           t.r.run();                 
    }

对于下面的情况,时间大约为7000毫秒。

 for (Task t : taskList) {        
           t.run();                                                              
 }

这并不是孤立发生的,而是在执行器的代码内部发生的。我想知道有没有人能给出提示,可能会发生什么?

对于这个测试用例,传递的可运行对象如下:

class Tester implements Runnable {                                                 
  int i;                                                                           

  public Tester(int i) {                                                           
    this.i = i;                                                                    
  }                                                                                

  @Override                                                                        
  public void run() {                                                              
    //System.out.println(i);                                                       
    for (int j = 0; j < 1000000; j++) {                                            
      i = j;                                                                       
    }                                                                              
  }                                                                                

  public int getI() {                                                              
    return i;                                                                      
  }  

供参考,这段代码可以在github.com/sharvanath/TempThreadPool上找到。运行ThreadPoolTest以获取执行结果。现在尝试更改ThreadPool.java的第41行,看看魔术般的效果。


1
你在这里发布的代码应该(几乎)没有差别。其他代码中一定有什么问题。 - Andy Turner
5
不应该分享整个代码 - 应该包含一个最小化的示例以展示问题。 - Jon Skeet
与不调用方法相比,调用一个什么也不做的方法的成本非常高。然而,什么也不做的方法非常罕见,计时它们是完全没有意义的。 - user207421
为了方便参考,我将代码放在了这个临时的Github仓库中:https://github.com/sharvanath/TempThreadPool - Sharvanath
Tester.run 中的代码很容易被编译器优化掉。再次问一下,List<Task> 中有多少个元素(Kenny Tai Huynh 问过了)? - Dirk Herrmann
显示剩余6条评论
1个回答

1
请注意,在Java中进行微观管理时,您必须使用一些技巧,因为JVM可以使用JIT编译器实时优化代码并执行许多您不知道的技巧,并且似乎您的测试实际上没有做所有必需的事情来让JVM替您完成此工作。(在测试前必须热身,转义排列,死代码等)。
一个很好的起点是阅读这个主题-如何在Java中编写正确的微基准测试? 另外,我建议您使用JMH框架进行此类测试,该框架已经在其源代码中具有许多使用示例。

如果您提供链接,请复制相关部分,因为链接可能会失效。 - Johannes Jander

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接