10

Does setting and getting an object attribute using reflection (java.lang.reflect.Field get and set functions) rather than calling the set and get of the object itself, result in any significant performance differences?

MBZ
  • 26,084
  • 47
  • 114
  • 191
  • 1
    @Brad-Larson The question referenced as a duplicate is completely different than this. Please unmark this, or select a duplicate question that actually corresponds with this one. – Philip Guin Jul 27 '19 at 16:56
  • To clarify: the current marked duplicated is about _object creation and destruction_, whereas this question is specifically about reflectively getting/setting a field. – Philip Guin Jul 27 '19 at 18:02
  • 1
    @Philip - I've reopened this. The reason this had been marked as a duplicate originally was that [the accepted answer to this question](https://stackoverflow.com/a/12996082/19679) (now deleted) was copy-and-paste plagiarized from the target question. In cases like this, I usually delete the plagiarized answer and mark the question as a duplicate of where the answer was sourced from. That usually works, but not always. – Brad Larson Jul 31 '19 at 16:24

3 Answers3

6

Yes, the benchmark is easy to write in 15 minutes.

Generated code is better, even if you cache the reflective accessors, I have tried it.

Here it is under Java 7 64 bits:

import java.lang.reflect.Field;

class Data {
   public double _value;
   public double getValue()               { return _value; }
   public void   setValue( double value ) { _value = value; }
}

public class Reflect {
   public static final int LOOP_COUNT = 100_000_000;
   public static void main( String[] args ) throws Throwable {
      Data d = new Data();
      long start = System.currentTimeMillis();
      for( int i = 0; i < LOOP_COUNT; ++i ) {
         d.setValue( i );
      }
      System.err.println( System.currentTimeMillis() - start );
      Field field = Data.class.getDeclaredField( "_value" );
      start = System.currentTimeMillis();
      for( int i = 0; i < LOOP_COUNT; ++i ) {
         field.set( d, new Double( i ));
      }
      System.err.println( System.currentTimeMillis() - start );

      field.setAccessible( true ); // Optimization
      start = System.currentTimeMillis();
      for( int i = 0; i < LOOP_COUNT; ++i ) {
         field.set( d, new Double( i ));
      }
      System.err.println( System.currentTimeMillis() - start );
   }
}

Result:

20
37381
1677

Ratio is near 1870 w/o accessible flag set. Setting it makes ratio drop to 83.

Aubin
  • 14,617
  • 9
  • 61
  • 84
  • What the!!? Does that compile? The integer literal with underscores? – Martijn Courteaux Oct 21 '12 at 08:46
  • 2
    I see. It is a Java 7 feature. Nice. http://docs.oracle.com/javase/7/docs/technotes/guides/language/underscores-literals.html – Martijn Courteaux Oct 21 '12 at 08:48
  • 1
    your benchmark shows how Java JIT can optimize tight loops, not getter/setter performance. – danbst Jul 30 '14 at 19:31
  • 1
    Nice benchmark. Inspired by this [blog entry](http://www.cowtowncoder.com/blog/archives/2010/04/entry_396.html) added `field.setAccessible(true);` before the loop and ratio went down from 1870 to 83 -> this sets the reflection in different light already. – botchniaque Oct 13 '14 at 06:58
  • Thanks botchniaque, I have commited your suggestion. Nice to know. – Aubin Oct 13 '14 at 18:41
  • 1
    Although the conclusion is correct and reflection almost always is the slower option, it is not as dramatically as in this benchmark's result. The compiler optimizer can easily see that the result value of _value will be LOOP_COUNT - 1 in this case and will almost certainly not execute the loop. In case reflection is used the optimizer's task is much harder since it can not easily check side effects of the "invocation". – Bas Goossen Oct 19 '17 at 08:12
  • 1
    Why are you allocating a boxed Double with each iteration of the field get/set version? That's obviously going to _heavily_ skew your results once the GC kicks in. Also, you should use `System.nanoTime()`, not `System.currentTimeMillis()`, as the former is intended for deltas and precision, the latter for some notion of user-facing 'current time'. – Philip Guin Jul 27 '19 at 16:48
  • 1
    I executed the same experiment, replacing `double` for `Double` in the `Data` class (to avoid autoboxing), moving the `setAccessible(true)` before the 2ND loop and changing `d.setValue(i)` for `d.setValue(new Double(i))` in the first loop ... I ran the experiment several times, and now the results are pretty close (73, 92, 84) ... – Carlitos Way Nov 19 '19 at 16:38
1

I ran a slightly modified version:

public class Test {

    private static class Data {

        public double _value;

        public void setValue(double value) {
            _value = value;
        }
    }

    public static final int LOOP_COUNT = 100_000_000;

    public static void main(String[] args) throws Throwable {

        Data d = new Data();

        Random testing = new Random(5);
        long start = System.currentTimeMillis();
        for (int i = 0; i < LOOP_COUNT; ++i) {
            d.setValue(testing.nextDouble());
        }
        System.err.println(System.currentTimeMillis() - start);

        Field field = Data.class.getDeclaredField("_value");

        testing = new Random(5);
        start = System.currentTimeMillis();
        for (int i = 0; i < LOOP_COUNT; ++i) {
            field.setDouble(d, testing.nextDouble());
        }
        System.err.println(System.currentTimeMillis() - start);

        testing = new Random(5);
        field.setAccessible(true); // Optimization
        start = System.currentTimeMillis();
        for (int i = 0; i < LOOP_COUNT; ++i) {
            field.setDouble(d, testing.nextDouble());
        }
        System.err.println(System.currentTimeMillis() - start);
    }
}

Result (on Java 1.8.0_121 and my PC):

2714
2741
2625

Seems like reflection is actually faster. Did not expect this...

noFearOfBeer
  • 77
  • 1
  • 7
0

Yes it does make a significant performance difference, and there are lots on benchmarking results on the web to support this.

For example: http://www.cowtowncoder.com/blog/archives/2007/02/entry_32.html - which seems to be saying that a reflective call to a get or set method is ~50 times slower than accessing / updating the field directly. (And getting / setting using Field methods is slower still.)

Now these results are rather old, and (apparently) the performance of reflection has been improved in recent HotSpot JVMs. Even so, a rough rule of thumb is "an order of magnitude or more slower".

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216