Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic Invoke feature in java

Tags:

java

If we use Dynamic Invoke, is there any performance improvement in the application? If not what is the its advantage?

like image 950
user556913 Avatar asked Dec 29 '10 09:12

user556913


2 Answers

So the final answer is this:

Invoke dynamic with callsites is faster than traditional method calls.

The only thing faster for field access is accessing the field directly without any method calls and using unsafe.

The answer is no invoke dynamic is not slow. It is fast. It is very fast.

Newer Update:

Another update (later that very Sunday) I changed the code to use a callsite for dynamic invoke and that changed the time quite a bit.

(All runs with JDK 1.8 build 94)

java version "1.8.0-ea"
Java(TM) SE Runtime Environment (build 1.8.0-ea-b94)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b36, mixed mode)

1,000 runs

description                                 duration in nanoseconds
regular method call time                    2095
invoke dynamic method call time             1098
reflection method call time                 3104

field method invoke dynamic call time       1165
field method invoke reflection call time    689
unsafe field access time                    94
direct field access (baseline)              92

10,000 runs

description                               duration in nanoseconds
regular method call time                  68
invoke dynamic method call time           43
reflection method call time               202

field method invoke dynamic call time     42
field method invoke reflection call time  45
unsafe field access time                  87
direct                                    86

100,000 runs

description                                 duration in nanoseconds
regular method call time                    70
invoke dynamic method call time             44
reflection method call time                 249

field method invoke dynamic call time       45
field method invoke reflection call time    47
unsafe field access time                    88
direct                                      36

1,000,000 runs

description                                 duration in nanoseconds
regular method call time                    11
invoke dynamic method call time             6
reflection method call time                 12

field method invoke dynamic call time       6
field method invoke reflection call time    4
unsafe field access time                    1
direct                                      0

10,000,000 runs

description                                 duration in nanoseconds
regular method call time                    9
invoke dynamic method call time             6
reflection method call time                 25

field method invoke dynamic call time       6
field method invoke reflection call time    4
unsafe field access time                    1
direct                                      0

100,000,000 runs

description                                 duration in nanoseconds
regular method call time                    9
invoke dynamic method call time             6
reflection method call time                 12

field method invoke dynamic call time       6
field method invoke reflection call time    4
unsafe field access time                    1
direct                                      0

Updated code that uses call-site with invoke dynamic

//fieldName is the reflection field (example below how to look it up and change its access)
MethodHandle methodHandleFieldDirect = lookup.unreflectGetter(fieldName);
CallSite callSiteField = new ConstantCallSite(methodHandleFieldDirect);
methodHandleFieldDirect = callSiteField.dynamicInvoker();
name = (String) methodHandleFieldDirect.invokeExact(new Employee());


//Lookup invoke dynamic
methodType = MethodType.methodType(String.class);
methodHandle = lookup.findVirtual(Employee.class, "getName", methodType);
CallSite callSiteMethod = new ConstantCallSite(methodHandleFieldDirect);
methodHandle = callSiteMethod.dynamicInvoker();

To see the rest, you have to see the blog entry. There are examples of using unsafe, reflection, callsites, invoke dynamic and the rest. :)

Callsites are important for speeding up invoke dynamic.

http://rick-hightower.blogspot.com/2013/10/java-invoke-dynamic-examples-java-7.html

Update (older update):

I took out the hashCode and count code, which I added because reflection was so fast that I thought the loops were getting JITTed out altogether somehow:

10 million runs with count/hashcode removed employee.getName()
  • regular method call time = 25 nanoseconds
  • invoke dynamic method call time = 18 nanoseconds **
  • reflection method call time = 36 nanoseconds
  • field method invoke dynamic call time = 8 nanoseconds
  • field method reflection call time = 6 nanoseconds
10 thousand runs with count/hashcode removed employee.getName()
  • regular method call time = 70 nanoseconds **
  • invoke dynamic method call time = 84 nanoseconds
  • reflection method call time = 211 nanoseconds
  • field method invoke dynamic call time = 153 nanoseconds
  • field method reflection call time = 40 nanoseconds

There is a performance improvement over reflection but it is complicated. It depends on how many times you make the calls. You probably only care if the code is in a tight loop.

The benchmarks that I have seen recently show 15x improvement over plain reflection, and only 2.5x slower than invoking a method in the normal way. But you know the old saying, believe nothing you hear and only 1/2 of what you read.

I figure I would try it out.

I have been fooling around with reflection and invokedynamic for a bit now. see A short write up on invoke dynamic.

Here are the results that I get using JDK 1.8 build 94.

One million calls (results are in nano seconds):

10,000 thousand calls

regular method call time        = 103
invoke dynamic method call time = 116
reflection method call time     = 252

100,000 thousand calls (after warmup).

regular method call time        = 46
invoke dynamic method call time = 112
reflection method call time     = 171

1,000,000 calls

regular method call time        = 23
invoke dynamic method call time = 35
reflection method call time     = 30

Reflection is faster at 1M than invoke dynamic. Ummm... Odd.

10,000,000 calls

regular method call time        = 34
invoke dynamic method call time = 24
reflection method call time     = 43

Now invoke dynamic is faster than a regular method call!

Now 100,000,000

regular method call time        = 22
invoke dynamic method call time = 24
reflection method call time     = 28

At this point, the JIT compiler takes all of the pain away. If you can't spare an extra 2 to 6 nano seconds, then you need to have some coping skills.

Here is the code to recreate the tests (also follow the above link):

    MethodHandles.Lookup lookup = MethodHandles.lookup();
    Class thisClass = lookup.lookupClass();  // (who am I?)

    MethodType methodType;
    MethodHandle methodHandle;

Create an employee object.

    Employee employee = new Employee();

Look up getName with invoke dynamic (see blog for a lot more examples, link above).

    //Lookup invoke dynamic
    methodType = MethodType.methodType(String.class);
    methodHandle = lookup.findVirtual(Employee.class, "getName", methodType);
    name = (String) methodHandle.invokeExact(new Employee());
    System.out.println("invoke dynamic " + name);

Look up Employee.getName with reflection.

    //Lookup reflection
    Method method = Employee.class.getMethod("getName", new Class<?>[]{});
    name = (String) method.invoke(new Employee());
    System.out.println("reflection " + name);

Start time, end time, number of iterations (times), calculated times, the usual suspects.

    long start = 0;
    long end = 0;
    long times = 100_000_000;
    long regularTime;
    long invokeDynamicTime;
    long reflectionTime;
    long count=0;

Now let's warm up the JVM.

    //warm up
    for (int index =0 ; index < times; index++) {
        employee.getName();
        name = (String) methodHandle.invokeExact(employee);
        name = (String) method.invoke(employee);
    }

Let's calculate regular method invocation.

    start = System.nanoTime();
    for (int index =0 ; index < times; index++) {
        name = employee.getName();
        count += name.hashCode();
    }
    count=0;
    end = System.nanoTime();
    regularTime = end - start;
    System.out.printf("regular method call time        = %d\n", regularTime/times);

P.S. I added the count, so my code would not get jitted away somehow.

Now let's calculate invokeDyanmic time.

    start = System.nanoTime();
    for (int index =0 ; index < times; index++) {
        name = (String) methodHandle.invokeExact(employee);
        count += name.hashCode();
    }
    count=0;
    end = System.nanoTime();
    invokeDynamicTime = end - start;

    System.out.printf("invoke dynamic method call time = %d\n", invokeDynamicTime/times);

Now let's calculate reflection time.

    start = System.nanoTime();
    for (int index =0 ; index < times; index++) {
        name = (String) method.invoke(employee);
        count += name.hashCode();
    }
    count=0;
    end = System.nanoTime();
    reflectionTime = end - start;
    System.out.printf("reflection method call time     = %d\n", reflectionTime/times);

I decided to add one more to the mix. If you really just want the property, then what if you just access the field directly.

10,000

regular method call time                  = 109
invoke dynamic method call time           = 115
reflection method call time               = 236
field method invoke dynamic call time     = 178
field method reflection call time         = 709

100_000

regular method call time                  = 49
invoke dynamic method call time           = 118
reflection method call time               = 312
field method invoke dynamic call time     = 75
field method reflection call time         = 158

1_000_000

regular method call time                  = 28
invoke dynamic method call time           = 41
reflection method call time               = 30
field method invoke dynamic call time     = 11
field method reflection call time         = 18

10_000_000

regular method call time                  = 28
invoke dynamic method call time           = 41
reflection method call time               = 30
field method invoke dynamic call time     = 11
field method reflection call time         = 18

100_000_000

regular method call time                  = 40
invoke dynamic method call time           = 25
reflection method call time               = 44
field method invoke dynamic call time     = 10
field method reflection call time         = 9

Ok, here is the code for the field access which is 4x faster than using employee.getName().

    long start = 0;
    long end = 0;
    long times = 10_000_000;
    long regularTime;
    long invokeDynamicTime;
    long reflectionTime;
    long invokeDynamicTimeUsingField;
    long fieldDirect;

    long count=0;

    //warm up
    for (int index =0 ; index < times; index++) {
        employee.getName();
        name = (String) methodHandle.invokeExact(employee);
        name = (String) method.invoke(employee);
        name = (String) methodHandleFieldDirect.invokeExact(employee);
    }


    start = System.nanoTime();
    for (int index =0 ; index < times; index++) {
        name = employee.getName();
        count += name.hashCode();
    }
    count=0;
    end = System.nanoTime();
    regularTime = end - start;
    System.out.printf("    regular method call time                  = %d\n", regularTime/times);

    start = System.nanoTime();
    for (int index =0 ; index < times; index++) {
        name = (String) methodHandle.invokeExact(employee);
        count += name.hashCode();
    }
    count=0;
    end = System.nanoTime();
    invokeDynamicTime = end - start;

    System.out.printf("    invoke dynamic method call time           = %d\n", invokeDynamicTime/times);

    start = System.nanoTime();
    for (int index =0 ; index < times; index++) {
        name = (String) method.invoke(employee);
        count += name.hashCode();
    }
    count=0;
    end = System.nanoTime();
    reflectionTime = end - start;
    System.out.printf("    reflection method call time               = %d\n", reflectionTime/times);


    start = System.nanoTime();
    for (int index =0 ; index < times; index++) {
        name = (String) methodHandleFieldDirect.invokeExact(employee);
        count += name.hashCode();
    }
    count=0;
    end = System.nanoTime();
    invokeDynamicTimeUsingField = end - start;
    System.out.printf("    field method invoke dynamic call time     = %d\n", invokeDynamicTimeUsingField/times);

    //
    start = System.nanoTime();
    for (int index =0 ; index < times; index++) {
        name = (String) fieldName.get(employee);
        count += name.hashCode();
    }
    count=0;
    end = System.nanoTime();
    fieldDirect = end - start;
    System.out.printf("    field method reflection call time         = %d\n", fieldDirect/times);

}

Now the actual field reflection / invoke dynamic code:

    Employee employee = new Employee();

    fieldName = null;
    for (Field field : Employee.class.getDeclaredFields()) {
        if (field.getName().equals("name")) {
            fieldName = field;
            fieldName.setAccessible(true);
            break;
        }
    }
    MethodHandle methodHandleFieldDirect = lookup.unreflectGetter(fieldName);
    name = (String) methodHandleFieldDirect.invokeExact(new Employee());
    System.out.println("method handle for field direct " + name);

    //Lookup invoke dynamic
    methodType = MethodType.methodType(String.class);
    methodHandle = lookup.findVirtual(Employee.class, "getName", methodType);
    name = (String) methodHandle.invokeExact(new Employee());
    System.out.println("invoke dynamic " + name);

    //Lookup reflection
    Method method = Employee.class.getMethod("getName", new Class<?>[]{});
    name = (String) method.invoke(new Employee());
    System.out.println("reflection " + name);
like image 174
RickHigh Avatar answered Oct 24 '22 09:10

RickHigh


I guess OP means InvokeDynamic as mentioned here http://java.sun.com/developer/technicalArticles/DynTypeLang/index.html for method invokation. I have never used myself but this post looks very descriptive it quotes

"The performance difference may not matter for a library doing a few reflected calls, especially if those calls are mostly to dynamically set up a static structure in memory against which it can make normal calls. But in a dynamic language, where every call must use these mechanisms, it's a severe performance hit."

like image 34
Nishant Avatar answered Oct 24 '22 09:10

Nishant