Reputation: 146
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
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:
Some comments on your approach to measuring performance on the JVM:
println
operation, which takes on the order of microseconds at least;Upvotes: 5
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
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