6

以下代码测量从接口Handler调用 100 次方法handle(Object o)所需的时间(是的,这是质量差的分析):

package test;

import java.util.LinkedList;

public class Test {

    static int i = 0;

    private interface Handler {
        public void handle(Object o);
    }
    private static class SuperHandler implements Handler {
        public void handle(Object o) { i += 1; }
    }
    private static class NoSuperHandler implements Handler {
        public void handle(Object o) { i += 1; }
    }
    private static class LulSuperHandler implements Handler {
        public void handle(Object o) { i += 1; }
    }
    private static class LilSuperHandler implements Handler {
        public void handle(Object o) { i += 1; }
    }
    private static class LolSuperHandler implements Handler {
        public void handle(Object o) { i += 1; }
    }
    private static class LalSuperHandler implements Handler {
        public void handle(Object o) { i += 1; }
    }
    private static class LylSuperHandler implements Handler {
        public void handle(Object o) { i += 1; }
    }
    private static class LzlSuperHandler implements Handler {
        public void handle(Object o) { i += 1; }
    }

    public static void main(String[] args) {
        LinkedList<Handler> ll = new LinkedList<Handler>();
        for(int j = 0; j < 100; j++) {
            if((j % 8) == 0) ll.add(new SuperHandler());
            if((j % 8) == 1) ll.add(new NoSuperHandler());
            if((j % 8) == 2) ll.add(new LulSuperHandler());
            if((j % 8) == 3) ll.add(new LilSuperHandler());
            if((j % 8) == 4) ll.add(new LolSuperHandler());
            if((j % 8) == 5) ll.add(new LalSuperHandler());
            if((j % 8) == 6) ll.add(new LylSuperHandler());
            if((j % 8) == 7) ll.add(new LzlSuperHandler());
        }
        long begin = System.currentTimeMillis();
        for(int j = 0; j < 1000000; j++) for(Handler h: ll) h.handle(null);
        System.out.println("time in ms: " + (System.currentTimeMillis() - begin));
        System.out.println("i: " + i);
    }
}

事实是,如果LinkedList仅包含一种Handler,例如SuperHandler,则执行时间比它们是 2、3 等不同种类的Handler时要短。每次我在列表中添加一种新的Handler时,性能都会下降。

例如,当我只更改这部分时,我会得到比上面更好的性能:

for(int j = 0; j < 100; j++) {
    if((j % 2) == 0) ll.add(new SuperHandler());
    if((j % 2) == 1) ll.add(new NoSuperHandler());
}

这里有特殊的优化操作吗?JAVA 架构中的哪些性能会降低?我的测试是错误的,因为未使用的Handler被编译器“删除”或“隐藏”了吗?(我正在使用来自 Oracle 的 Linux Ubuntu - JAVA 1.7)

4

2 回答 2

10

这里有特殊的优化操作吗?

是的。Hotspot 在处理虚方法方面非常聪明。如果一个接口只有几个实现,并且这些实现很小,它可以通过检查正确的类型并内联代码来避免完整的 vtable-lookup。

当你有几个不同的实现时,它会回到 vtable 实现。(Hotspot 足够聪明,可以撤消不再实用的优化。我很震惊这一切都挂在一起,但显然确实如此。)

请注意,这与列表中有多少不同的类无关——这里还有更多内容。有关详细信息,请参阅彼得的答案。

于 2012-12-03T19:38:04.800 回答
4

我同意乔恩的回答,但我相信代码点调用的类型数量会有所不同。在下面的示例中,加载了所有 8 个类,并且针对列表中相同数量的元素运行相同的代码,除了一个列表有 8 个,另一个有 2 种不同的类型。

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class Test {

    static int i = 0;

    private interface Handler {
        public void handle();
    }

    public static void main(String[] args) {
        List<Handler> ll8 = new ArrayList<Handler>();
        for (int j = 0; j < 128; j += 8) {
            ll8.add(new SuperHandler());
            ll8.add(new NoSuperHandler());
            ll8.add(new LulSuperHandler());
            ll8.add(new LilSuperHandler());
            ll8.add(new LolSuperHandler());
            ll8.add(new LalSuperHandler());
            ll8.add(new LylSuperHandler());
            ll8.add(new LzlSuperHandler());
        }
        List<Handler> ll2 = new ArrayList<Handler>();
        for (int j = 0; j < 128; j += 2) {
            ll2.add(new SuperHandler());
            ll2.add(new NoSuperHandler());
        }
        for (int j = 0; j < 5; j++) {
            test8(ll8);
            test8a(ll8);
            test2(ll2);
        }
        System.out.println("i: " + i);
    }

    private static void test8(List<Handler> ll8) {
        long begin = System.nanoTime();
        for (int j = 0; j < 1000000; j++) for (Handler h : ll8) h.handle();
        System.out.println("8 classes, time in ms: " + (System.nanoTime() - begin) / 100000 / 10.0);
    }

    private static void test8a(List<Handler> ll8) {
        long begin = System.nanoTime();
        for (int j = 0; j < 1000000; j++)
            for (int k = 0; k < ll8.size(); k += 8) {
                ll8.get(k + 0).handle();
                ll8.get(k + 1).handle();
                ll8.get(k + 2).handle();
                ll8.get(k + 3).handle();
                ll8.get(k + 4).handle();
                ll8.get(k + 5).handle();
                ll8.get(k + 6).handle();
                ll8.get(k + 7).handle();
            }
        System.out.println("8 classes unrolled, time in ms: " + (System.nanoTime() - begin) / 100000 / 10.0);
    }

    private static void test2(List<Handler> ll2) {
        long begin = System.nanoTime();
        for (int j = 0; j < 1000000; j++) for (Handler h : ll2) h.handle();
        System.out.println("2 classes, time in ms: " + (System.nanoTime() - begin) / 100000 / 10.0);
    }

    private static class SuperHandler implements Handler {
        public void handle() { i += 1; }
    }

    private static class NoSuperHandler implements Handler {
        public void handle() { i += 1; }
    }

    private static class LulSuperHandler implements Handler {
        public void handle() { i += 1; }
    }

    private static class LilSuperHandler implements Handler {
        public void handle() { i += 1; }
    }

    private static class LolSuperHandler implements Handler {
        public void handle() { i += 1; }
    }

    private static class LalSuperHandler implements Handler {
        public void handle() { i += 1; }
    }

    private static class LylSuperHandler implements Handler {
        public void handle() { i += 1; }
    }

    private static class LzlSuperHandler implements Handler {
        public void handle() { i += 1; }
    }
}

印刷

8 classes, time in ms: 1467.9
8 classes unrolled, time in ms: 144.7
2 classes, time in ms: 515.8
8 classes, time in ms: 1455.1
8 classes unrolled, time in ms: 126.2
2 classes, time in ms: 509.6
8 classes, time in ms: 1234.1
8 classes unrolled, time in ms: 107.8
2 classes, time in ms: 274.3
8 classes, time in ms: 1212.0
8 classes unrolled, time in ms: 108.1
2 classes, time in ms: 273.0
8 classes, time in ms: 1208.8
8 classes unrolled, time in ms: 107.8
2 classes, time in ms: 274.5
i: 1920000000
于 2012-12-03T20:51:36.080 回答