Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: Getter and setter faster than direct access?

I tested the performance of a Java ray tracer I'm writing on with VisualVM 1.3.7 on my Linux Netbook. I measured with the profiler.
For fun I tested if there's a difference between using getters and setters and accessing the fields directly. The getters and setters are standard code with no addition.

I didn't expected any differences. But the directly accessing code was slower.

Here's the sample I tested in Vector3D:

public float dot(Vector3D other) {
    return x * other.x + y * other.y + z * other.z;
}

Time: 1542 ms / 1,000,000 invocations

public float dot(Vector3D other) {
    return getX() * other.getX() + getY() * other.getY() + getZ() * other.getZ();
}

Time: 1453 ms / 1,000,000 invocations

I didn't test it in a micro-benchmark, but in the ray tracer. The way I tested the code:

  • I started the program with the first code and set it up. The ray tracer isn't running yet.
  • I started the profiler and waited a while after initialization was done.
  • I started a ray tracer.
  • When VisualVM showed enough invocations, I stopped the profiler and waited a bit.
  • I closed the ray tracer program.
  • I replaced the first code with the second and repeated the steps above after compiling.

I did at least run 20,000,000 invocations for both codes. I closed any program I didn't need. I set my CPU on performance, so my CPU clock was on max. all the time.
How is it possible that the second code is 6% faster?

like image 551
Mr.Yeah Avatar asked May 29 '14 10:05

Mr.Yeah


People also ask

Does getter and setter speed up compilation?

Getters and setters can speed up compilation. Getters and setters provide encapsulation of behavior. Getters and setters provide a debugging point for when a property changes at runtime.

What is the advantage of using getter and setter?

The getter and setter method gives you centralized control of how a certain field is initialized and provided to the client, which makes it much easier to verify and debug. To see which thread is accessing and what values are going out, you can easily place breakpoints or a print statement.

Are getters and setters good practice?

Every getter and setter in your code represents a failure to encapsulate and creates unnecessary coupling. Encapsulation is the most important idea of Object Oriented Programming. It essentially boils down to hiding complexity by wrapping it neatly inside classes.

Should you always use getters and setters?

Using getters and setters, is always, in my opinion good practice. One thing you should avoid is to have external entities mess with the internal structure of your class at will. Typical example, consider having a dateOfBirth parameter.


2 Answers

I did done some micro benchmarking with lots of JVM warm up and found the two approaches take the exact same amount of execution time.

This happens because the JIT compiler is in-lining the getter method with a direct access to the field thus making them identical bytecode.

like image 195
Bohemian Avatar answered Oct 13 '22 09:10

Bohemian


Thank you all for helping me answering this question. In the end, I found the answer.

First, Bohemian is right: With PrintAssembly I checked the assumption that the generated assembly codes are identical. And yes, although the bytecodes are different, the generated codes are identical.
So masterxilo is right: The profiler have to be the culprit. But masterxilo's guess about timing fences and more instrumentation code can't be true; both codes are identical in the end.

So there's still the question: How is it possible that the second code seems to be 6% faster in the profiler?

The answer lies in the way how VisualVM measures: Before you start profiling, you need calibration data. This is used for removing the overhead time caused by the profiler.
Although the calibration data is correct, the final calculation of the measurement is not. VisualVM sees the method invocations in the bytecode. But it doesn't see that the JIT compiler removes these invocations while optimizing.
So it removes non-existing overhead time. And that's how the difference appears.

like image 23
Mr.Yeah Avatar answered Oct 13 '22 08:10

Mr.Yeah