Alvaro Lemos
Alvaro Lemos

Reputation: 146

Does java take time to call a method?

I was doing some experiments in java, and I've encountered a thing that is bugging me. I have realized that in java, when I use a method instead of direct code, it takes more time to process it.

I have the following code:

public static void main(String[] args) {
    long nanoSeconds = System.nanoTime();
    int i = foo();
    System.out.println(i);
    System.out.println("Elapsed Nanoseconds = " + (System.nanoTime() - nanoSeconds));
    nanoSeconds = System.nanoTime();
    int l = 10;
    i = l;
    System.out.println(i);
    System.out.println("Elapsed Nanoseconds = " + (System.nanoTime() - nanoSeconds));
}

public final static int foo() {
    int i = 10;
    return i;
}

It is a simple code divided in two parts. The first one measures the time of foo() and shows the returned value of foo(), and the second part does the same but without calling foo().

The result was the following:

10

Elapsed Nanoseconds = 601582

10

Elapsed Nanoseconds = 49343

So my question is if is there a way to not loose this performance?

Thanks all.

Upvotes: 6

Views: 2604

Answers (3)

Marko Topolnik
Marko Topolnik

Reputation: 200138

Here is your code measured on JMH:

@OutputTimeUnit(TimeUnit.NANOSECONDS)
@BenchmarkMode(Mode.AverageTime)
@OperationsPerInvocation(Measure.SIZE)
@Warmup(iterations = 10, time = 100, timeUnit=MILLISECONDS)
@Measurement(iterations = 5, time = 200, timeUnit=MILLISECONDS)
@State(Scope.Thread)
@Threads(1)
@Fork(1)
public class Measure
{
  public static final int SIZE = 1;

  @Benchmark public int call() {
    int i = foo();
    return i;
  }

  @Benchmark public int callDisableOptimization() {
    int i = fooDontInline();
    return i;
  }

  @Benchmark public int inline() {
    int i;
    int l = 10;
    i = l;
    return i;
  }

  static int foo() {
    int i = 10;
    return i;
  }

  @CompilerControl(CompilerControl.Mode.DONT_INLINE)
  static int fooDontInline() {
    int i = 10;
    return i;
  }
}

Explanation:

  • call corresponds to your first measurement, a regular call to foo();
  • inline corresponds to your second measurement, where you inline the logic of foo() with no calls involved;
  • callDisableOptimization is a special twist on the first case, where we used advanced low-level control to disable the automatic inlining by the JVM.

And these are the results:

Benchmark                              Mode  Samples  Score   Error  Units
o.s.Measure.call                       avgt        5  1,279 ± 0,114  ns/op
o.s.Measure.inline                     avgt        5  1,289 ± 0,100  ns/op
o.s.Measure.callDisableOptimization    avgt        5  3,167 ± 0,217  ns/op

Things to note:

  • there is actually no difference at all between your two pieces of code. The JIT-compiler will have an easy time inlining that method call for you, ending up with precisely the same code in both cases;
  • the added third case shows just how insignificant the overhead would be even without JIT compiler's inlining: a true method invocation incurs an overhead of less than 2 nanoseconds. Putting this in perspective, a million invocations would add just 2 milliseconds to the overall runtime.

Some comments on your approach to measuring performance on the JVM:

  • what you're trying to measure takes around a single nanosecond;
  • you are including that nanosecond together with a println operation, which takes on the order of microseconds at least;
  • you have not done any warmup at all;
  • you have failed to collect a statistically representative sample;
  • the time reported by your attempt is 6 milliseconds for the first measurement. It is off by a factor of 6 million from the real value;
  • the stark difference between your first and second mesauremnts is attributable to one-time initialization code which runs during the first measurement, an entirely irrelevant artefact;
  • etc.
  • etc.

Upvotes: 5

fge
fge

Reputation: 121702

You will not obtain any meaningful benchmark this way.

You don't account for the JIT.

The compiler will not perform any optimization in this regard, apart from very obvious ones; when it sees a method call in the source code, even if this method call always returns the same value, it will generate bytecode which invokes the method; when it sees a constant, it will generate an ldc (load constant) bytecode instruction.

BUT.

Then the JIT kicks in at some point. If it determines that a method call always returns the same result, then it will inline the call. At runtime. But this is only done after a certain amount of executions of that code are performed, and it always has a way back if it admits that it has missed at some point (this is backtracking).

And that is but one optimization a good JIT implementation can perform.

You want to watch this video. Long story short: with Oracle's JVM, optimization will start to kick in only after a piece of code will be executed 10000 times at least — for some definition of "piece of code".

Upvotes: 12

maxdev
maxdev

Reputation: 2566

A note: You can't measure time exactly enough for this kind of assumptions by just calling the method once. You'd have to at least do it multiple times to get a usable result.

Yes, calling a method is slower due to more instructions being involved. When the code is within the other function, it can simply be executed there. When a function is called, a new stack frame is created, and after returning removed. This takes a little time - at least until for example JIT kicks in and compiles said part. You should take a look at the JVM specifications for more information.

Upvotes: 0

Related Questions