views:

1279

answers:

2

I've seen other threads saying java reflection performance is 10-100x slower than when using non-reflection calls.

My tests in 1.6 have shown that this is not the case but I found some other interesting things that I need someone to explain to me.

I have objects that implement my interface. I did three things 1) using a reference to an Object I cast that object to the interface and call the method through the interface 2) using a reference to the actual object call the method directly and 3) call the method through reflection. I saw that #1 interface call was fastest followed closely by #3 reflection but I noticed that the direct method call was the slowest by a good margin.

I don't understand that, I would've expected the direct call to be fastest, then the interface, then reflection would be much much more slow.

Blah and ComplexClass are in a different package from the main class and both have a doSomething(int x) method that implements the interface and just prints the integer x.

Here are my results (times in ms, results very similar w/ multiple trials): calling a method directly: 107194 calling a method directly from an object cast to an interface: 89594 calling a method through reflection: 90453

Here is my code:

public class Main
{

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {
        Blah x = new Blah();
        ComplexClass cc = new ComplexClass();
        test((Object) x, cc);
    }

    public static void test(Object x, ComplexClass cc)
    {
        long start, end;
        long time1, time2, time3 = 0;
        int numToDo = 1000000;
        MyInterface interfaceClass = (MyInterface) x;

        //warming up the cache
        for (int i = 0; i < numToDo; i++)
        {
            cc.doSomething(i); //calls a method directly
        }

        start = System.currentTimeMillis();
        for (int i = 0; i < numToDo; i++)
        {
            cc.doSomething(i); //calls a method directly
        }
        end = System.currentTimeMillis();
        time1 = end - start;

        start = System.currentTimeMillis();
        for (int i = 0; i < numToDo; i++)
        {
            interfaceClass.doSomething(i); //casts an object to an interface then calls the method
        }
        end = System.currentTimeMillis();
        time2 = end - start;


        try
        {
            Class xClass = x.getClass();
            Class[] argTypes =
            {
                int.class
            };
            Method m = xClass.getMethod("doSomething", argTypes);
            Object[] paramList = new Object[1];
            start = System.currentTimeMillis();
            for (int i = 0; i < numToDo; i++)
            {
                paramList[0] = i;
                m.invoke(x, paramList); //calls via reflection
            }
            end = System.currentTimeMillis();
            time3 = end - start;

        } catch (Exception ex)
        {
        }

        System.out.println("calling a method directly: " + time1);
        System.out.println("calling a method directly from an object cast to an interface: " + time2);
        System.out.println("calling a method through reflection: " + time3);
    }
+6  A: 

Putting all the tests into the same program is a microbenchmarking mistake - there is a certain warm up associated with Java performance. This is the most important failing.

Put your tests in separate programs. Then run the tests several times, so you get a feeling for when warm up has finished and statistical significance.

Also you have a huge method containing your inner loop. Hotspot appears to be better at dealing with this than it used to be, but it still isn't good.

You should find that with -server calling a virtual method (even if loaded by a different class loader) in a tight loop gets completely optimised away. It therefore doesn't make much sense to say how much fast a direct call is than a reflective call.

Tom Hawtin - tackline
what do you mean there is a huge method containing the inner loop? Are you saying that the test method itself is too big? Why is that a problem?what is -server?
jbu
and I see your point with the problem that the individual tests aren't in their own methods. Though I have a feeling I'll see similar results. Will make a separate post probably once I get those results.
jbu
You've got a big test method which iterates around loops millions of times. -server is the go faster switch (but increases start up times, and is not necessarily present, particularly on the Windows JRE). The tests should be in their own process. Run the program fresh for each test.
Tom Hawtin - tackline
All excellent points Tom
matt b
+2  A: 

First, reflection got much faster in the most recent JDKs. Second I expect that the Hot Spot compiler is going to optimize all of these calls to roughly the code. It can do run time analysis to realize that you are invoking the same function over and over again so it can optimize out the reflection (and virtual function call). Same thing with the interface example.

hacken
+1 for reflection improvement in later jdk's.
Thorbjørn Ravn Andersen