How can I improve performance of Field.set (perhap using MethodHandles)?

前端 未结 4 2004
渐次进展
渐次进展 2020-12-02 10:52

I\'m writing some code that calls Field.set and Field.get many many thousands of times. Obviously this is very slow because of the reflection.

I want to

4条回答
  •  心在旅途
    2020-12-02 11:16

    EDIT thanks to holger I noticed that I really should have used invokeExact, so I decided to remove the stuff about other jdks and use invokeExact only... using -server or not still does not really make a difference for me though

    The main difference between using reflection and using MethodHandles is that for reflection you have a security check for every call, in case of MethodHandles, only for the creation of the handle.

    If you look at this

    class Test {
        public Object someField;
        public static void main(String[] args) throws Exception {
            Test t = new Test();
            Field field = Test.class.getDeclaredField("someField");
            Object value = new Object();
            for (int outer=0; outer<50; outer++) {
                long start = System.nanoTime();
                for (int i=0; i<100000000; i++) {
                    field.set(t, value);
                }
                long time = (System.nanoTime()-start)/1000000;
                System.out.println("it took "+time+"ms");
            }
        }
    }
    

    Then I get on my computer times 45000ms on jdk7u40 (jdk8 and pre 7u25 perform much better though)

    Now let's look at the same program using handles

    class Test {
        public Object someField;
        public static void main(String[] args) throws Throwable {
            Test t = new Test();
            Field field = Test.class.getDeclaredField("someField");
            MethodHandle mh = MethodHandles.lookup().unreflectSetter(field);
            Object value = new Object();
            for (int outer=0; outer<50; outer++) {
                long start = System.nanoTime();
                for (int i=0; i<100000000; i++) {
                    mh.invokeExact(t, value);
                }
                long time = (System.nanoTime()-start)/1000000;
                System.out.println("it took "+time+"ms");
            }
        }
    }
    

    7u40 says roughly 1288ms. So I can confirm Holger's 30 times on 7u40. On 7u06 this code handles would be slower because reflection was several times faster and on jdk8 everything is new again.

    As for why you didn't see an improvement... difficult to say. What I did was microbenchmarking. That doesn't tell anything about a real application at all. But using those results I would assume you either use an old jdk version, or you don't reuse the handle often enough. Because while executing a handle can be faster, the creation of the handle can cost much more then the creation of a Field.

    Now the biggest problem point... I did see you want this for google appengine... And I must say, you can test locally as much as you want, what counts in the end is what the performance of the application on the google site will be. Afaik they use a modified OpenJDK, but what version with what modification they don't say. With Jdk7 being that unstable you could be unlucky or not. Maybe they added special code for reflection, then all bets are off anyway. And even ignoring that... maybe the payment model changed again, but usually you want to avoid datastore access by caching because it costs. If that still holds, is it then realistic that any handle will be called let's say 10.000 times on average?

提交回复
热议问题