0

I've been given a file with 200 fields whose values are retrieved using reflection. I've been asked to rewrite it because it's slow. The file has 200 String fields which are retrieved like this:

for (Field f : this.getClass().getFields())
        {
            try
            {
                Object o = f.get(this);
            }
        }

I've extrapolated the code into three classes for testing; one which uses "full" reflection, some reflection and no reflection.

"Full" reflection is the example listed above. It gets the fields, loops through them then gets the object associated with the field:

//Two hundred strings declared = "test String"

public void reflection()
            throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {

        int count = 0;
        long timer = System.nanoTime();
        for (Field f : this.getClass().getFields()) {
            count++;
            Object o = f.get(this);
        }

        System.out.println(count);
        System.out.print("Reflection: ");
        System.out.println(System.nanoTime() - timer);

    }

Less reflection involves holding putting the names of all the fields into an array and simply performing the get() method:

//Two hundreds Strings declared = "test string"
//array holding the names of all the strings
public void reflection()
        throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {

    int count = 0;
    long timer = System.nanoTime();

    for (String s : fieldnames) {
        Field field = this.getClass().getField(s);
        Object obj = field.get(this);
        count++;
    }
    System.out.println(count);
    System.out.print("Less reflection: ");
    System.out.println(System.nanoTime() - timer);

}

The final one contains absolutely no reflection. The two hundred strings are written out and then assigned to another string when running.

//Two hundred strings declared = "test String"
public void reflection()
            throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
    int count = 0;
    long timer = System.nanoTime();

    String newVariable1 = variable1;
    String newVariable2 = variable2;
    //continued for all two hundred

    System.out.print("No reflection: ");
    System.out.println(System.nanoTime() - timer);
}

When I run "full" reflection I get: Reflection: 1511155

When I run less reflection I get: Less reflection: 1811682

and when I run no reflection I get: No reflection: 199956

While these numbers vary, the trend is the same: my results show completely the opposite of what I expected! The less reflection you use the slower it gets? Am I using System.nanoTime() wrong? Have I been misinformed about reflection? Is the compiler playing another joke on me?

Nanor
  • 2,400
  • 6
  • 35
  • 66
  • How was it originally decided that it was slow and the fault was reflection? – Thorbjørn Ravn Andersen Oct 04 '16 at 14:23
  • Try doing the loop/processing 1000 times - you should get more reliable results. – OldCurmudgeon Oct 04 '16 at 14:25
  • no, you're just running the test not enough time to eliminate inherent measurement error. 200 cycles is basically a random number, 200,000 cycles is much better, and 2,000,000 cycle should be bang-on. – Marc B Oct 04 '16 at 14:26
  • I feel like inaccurate benchmarking aside, it does make sense that your second example could perform worse than the first. If anything, your second example actually works harder because each time you call `getField`, that method has to iterate through all the public fields in that class and its hierarchy (until it finds the one you're looking for), whereas the first example can do it all in one pass. As for your third example, I'm not sure there's enough information to go on... – sgbj Oct 04 '16 at 14:47
  • Following the guidelines of the marked answer and increasing the cycles show that the order from fastest to slowest is `no reflection > full reflection > less reflection` so the second example does indeed work harder. Looks like I'm writing a lot of things out rote! – Nanor Oct 04 '16 at 14:52

1 Answers1

2

Have I been misinformed about reflection?

No.

Regular Java is faster than reflective Java. (My memory1 is that the difference in performance is not as great as it used to be in older versions of Java, but there is a significant difference nonetheless.)

Is the compiler playing another joke on me?

No.

What you actually have here is an example of a poorly written "micro-benchmark" that is giving you misleading results.

Please read this Q&A - How do I write a correct micro-benchmark in Java?


1 - If someone can find a credible reference to back this up, please let me know.

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