Reputation: 1084
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 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?
Upvotes: 33
Views: 13589
Reputation: 1084
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.
Upvotes: 26
Reputation: 425378
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.
Upvotes: 43
Reputation: 13
In case you have not taken a course in Statistics, there is always variance in program performance no matter how well that it is written. The reason why these two methods seem to run at approximately the same rate is because the accessor fields only do one thing: They return a particular field. Because nothing else happens in the accessor method, both tactics pretty much do the same thing; however, in case you know not about encapsulation, which is how well that a programmer hides the data (fields or attributes) from the user, a major rule of encapsulation is not to reveal internal data to the user. Modifying a field as public means that any other class can access those fields, and that can be very dangerous to the user. That is why I always recommend Java programmers to use accessor and mutator methods so that the fields will not get into the wrong hands.
In case you were curious about how to access a private field, you can use reflection, which actually accesses the data of a particular class so that you can mutate it if you really must do so. As a frivolous example, suppose that you knew that the java.lang.String class contains a private field of type char[] (that is, a char array). It is hidden from the user, so you cannot access the field directly. (By the way, the method java.lang.String.toCharArray() accesses the field for you.) If you wanted to access each character consecutively and store each character into a collection (for the sake of simplicity, why not a java.util.List?), then here is how to use reflection in this case:
/**
This method iterates through each character in a <code>String</code> and places each of them into a <code>java.util.List</code> of type <code>Character</code>.
@param str The <code>String</code> to extract from.
@param list The list to store each character into. (This is necessary because the compiler knows not which <code>List</code> to use, so it will automatically clear the list anyway.)
*/
public static void extractStringData(String str, List<Character> list) throws IllegalAccessException, NoSuchFieldException
{
java.lang.reflect.Field value = String.class.getDeclaredField("value");
value.setAccessible(true);
char[] data = (char[]) value.get(str);
for(char ch : data) list.add(ch);
}
As a sidenote, note that reflection takes a lot of performance out of your program. If there is a field, method, or inner or nested class that you must access for whatever reason (which is highly unlikely anyway), then you should use reflection. The main reason why reflection takes away precious performance is because of the relatively innumerable exceptions that it throws. I am glad to have helped!
Upvotes: 0