6

我正在做一些测试,以了解使用 getter/setter 和直接字段访问之间的速度差异。我写了一个简单的基准测试应用程序,如下所示:

public class FieldTest {

    private int value = 0;

    public void setValue(int value) {
        this.value = value;
    }

    public int getValue() {
        return this.value;
    }

    public static void doTest(int num) {
        FieldTest f = new FieldTest();

        // test direct field access
        long start1 = System.nanoTime();

        for (int i = 0; i < num; i++) {
            f.value = f.value + 1;
        }
        f.value = 0;

        long diff1 = System.nanoTime() - start1;

        // test method field access
        long start2 = System.nanoTime();

        for (int i = 0; i < num; i++) {
            f.setValue(f.getValue() + 1);
        }
        f.setValue(0);

        long diff2 = System.nanoTime() - start2;

        // print results
        System.out.printf("Field Access:  %d ns\n", diff1);
        System.out.printf("Method Access: %d ns\n", diff2);
        System.out.println();
    }

    public static void main(String[] args) throws InterruptedException {
        int num = 2147483647;

        // wait for the VM to warm up
        Thread.sleep(1000);

        for (int i = 0; i < 10; i++) {
            doTest(num);
        }
    }

}

每当我运行它时,我都会得到一致的结果,例如: http: //pastebin.com/hcAtjVCL

我想知道是否有人可以向我解释为什么字段访问似乎比 getter/setter 方法访问慢,以及为什么最后 8 次迭代执行得非常快。


编辑:考虑到assyliasStephen C评论后,我将代码更改为http://pastebin.com/Vzb8hGdc,结果略有不同:http: //pastebin.com/wxiDdRix

4

1 回答 1

9

解释是你的基准被打破了。

第一次迭代是使用解释器完成的。

Field Access:  1528500478 ns
Method Access: 1521365905 ns

第二次迭代由解释器开始,然后我们转向运行 JIT 编译的代码。

Field Access:  1550385619 ns
Method Access: 47761359 ns

其余的迭代都是使用 JIT 编译的代码完成的。

Field Access:  68 ns
Method Access: 33 ns

etcetera

它们快得令人难以置信的原因是 JIT 编译器已经优化了循环。它检测到它们没有对计算做出任何有用的贡献。(不清楚为什么第一个数字似乎总是比第二个更快,但我怀疑优化的代码是否以任何有意义的方式测量字段与方法访问。)


重新更新代码/结果:很明显,JIT 编译器仍在优化循环。

于 2013-02-12T12:04:09.507 回答