8

I have a thread executor implementation where the cost of a wrapper is showing up as very costly. There is a wrapper class Task which is defined as follows:

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

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

 List<task> taskList;

For following case the run time is ~800ms.

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

While for the following case it is ~7000ms.

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

It does not happen in isolation, but is happening inside the code of the executor. Just wondering if someone has a hint as to what might be going on?

The runnable being passed is the following for this test case:

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;                                                                      
  }  

For reference, the code can be found on github.com/sharvanath/TempThreadPool. Run the ThreadPoolTest for getting the result of the execution. Now try changing line 41 of ThreadPool.java and see the magic.

Sharvanath
  • 457
  • 4
  • 15
  • What does `run` do? Is it a no-op? – 5gon12eder Feb 15 '16 at 09:20
  • 1
    There should be (almost zero) difference for the code you have posted here. There must be something in other code. – Andy Turner Feb 15 '16 at 09:20
  • 5
    You shouldn't share the whole code - you should include a *minimal* example that demonstrates the problem. – Jon Skeet Feb 15 '16 at 09:21
  • what do you do in your Runnable class?! How many List do you have?! – Kenny Tai Huynh Feb 15 '16 at 09:22
  • How did you get the number? Are you sure that the experiment was setup correctly? – Andrey Taptunov Feb 15 '16 at 09:22
  • The cost of calling a method that does nothing is very large *compared to the cost of not calling it.* However methods that do nothing are extremely rare, and timing them is utterly pointless. – user207421 Feb 15 '16 at 09:24
  • Okay for reference, I added the code in this temporary github repo: https://github.com/sharvanath/TempThreadPool – Sharvanath Feb 15 '16 at 09:26
  • The code in Tester.run could easily be optimized away by a compiler. Again, how many elements List do you have (as asked by Kenny Tai Huynh)? – Dirk Herrmann Feb 15 '16 at 09:47
  • @DirkHerrmann I have linked the code to the problem. The size of list is usually usually around 2000 or so. – Sharvanath Feb 15 '16 at 09:49
  • If you want to measure execution times you have to create a microbenchmark. Use JMH for instance. Otherwise results will be almost random – Jakub Kubrynski Feb 15 '16 at 10:21
  • I think it's because the added complexity makes the JRE wait longer before compiling : With the `-Xcomp` runtime options (immediate compilation) the overhead disappears (at least with the 64 bits Oracle JRE 8) – bwt Feb 15 '16 at 11:38

1 Answers1

1

Please take into account that doing micromanaging in java you have to use some tricks, because jvm can optimize code on fly with JIT compiler and do a lot of tricks that you don't know about, and looks like your tests actually doesn't do all required things to let JVM do this job for you.(you have to do warm up before test, escape in linings, dead code and so on.)

Its a good point to start is to read this topic - How do I write a correct micro-benchmark in Java?

Also I advice you to use JMH framework for your this kind of tests, that already have lot of examples of it's usage in its source code.

Community
  • 1
  • 1