32

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?

Mr.Yeah
  • 1,054
  • 2
  • 9
  • 21
  • 1
    Did you read this question http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java -- Can you post the test code? – Marco Acierno May 29 '14 at 10:50
  • 3
    Were both the loops in a single program? If yes, swap the loops and check. – Dhrubajyoti Gogoi May 29 '14 at 10:50
  • 4
    possible duplicate of [Why does Method access seem faster than Field access?](http://stackoverflow.com/questions/14832007/why-does-method-access-seem-faster-than-field-access) – Artur Malinowski May 29 '14 at 10:56
  • @ArturMalinowski The accepted answer on that question only criticises the test but then even goes on to say that it doesn't explain why field access is slower after the test was improved. So I don't think it's useful to mark anything as a duplicate of that question. – Erwin Bolwidt May 29 '14 at 11:33
  • 1
    I added some details about how I did the measurement. It's different from the possible duplicates because I didn't do a micro-benchmark and I didn't test both codes at the same time. – Mr.Yeah May 29 '14 at 11:36
  • 4
    Perhaps the profiler is the culprit. It might put timing fences around every single access of a property, while it only puts one into the accessor function. Then the code accessing the fields directly has more instrumentation code in the code cache than the one using methods. – masterxilo Jun 02 '14 at 08:46
  • How is it possible that the second code has less instrumentation code than the first one? The methods also access the fields. So if the profiler puts timing fences around field accesses, it will affect both codes. – Mr.Yeah Jun 08 '14 at 09:37

3 Answers3

43

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.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • 1
    I looked at the bytecode. They're not identical. The bytecode of the first example contains stuff like `getfield #18 ` and the second contains `invokevirtual #67 ` at that position. – Mr.Yeah Jun 08 '14 at 09:22
  • 4
    @Mr.Yeah did you look at the compiled byte ode in the class file, which I am not talking about, or the new in-memory code that the JIT compiler replaced the compiled byte code with (which I'm not sure you can even get to)? – Bohemian Jun 08 '14 at 09:28
  • @Bohemian I looked into the bytecode of the class file. How did you look in the bytecode if you're talking about something else? – Mr.Yeah Jun 08 '14 at 10:48
  • 4
    I didn't look. I surmised. The JIT in-lines, rewrites, etc to the minimum equivalent code, which in the case of a simple getter is to replace with direct field access. The timing evidence I collected showed that whatever was there was the same for both, so the logical conclusion was the getter had been replaced by direct field access. – Bohemian Jun 08 '14 at 11:40
24

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.

Mr.Yeah
  • 1,054
  • 2
  • 9
  • 21
0

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!

user3709221
  • 13
  • 1
  • 4
  • The fields are encapsulated: The fields `x`, `y` and `z` are private. The reason why the first code works: The method `dot` is inside `Vector3D`. – Mr.Yeah Jun 08 '14 at 09:17