创建预先实现的java类的对象比创建自定义对象快得多?

时间:2013-05-18 07:49:34

标签: java performance oop

class DummyInteger {
    private int i;
    public DummyInteger(int i) {
        this.i = i;
    }
    public int getI() {
        return i;
    }
}

long start = System.nanoTime();
DummyInteger n = new DummyInteger(10);
long end = System.nanoTime();
long duration = end - start;
System.out.println(duration);

前面的代码产生以下输出:

341000

鉴于:

long start = System.nanoTime();
ArrayList a = new ArrayList();
long end = System.nanoTime();
long duration = end - start;
System.out.println(duration);

产生以下输出:

17000

现在,我的问题是,为什么我们在运行时间上观察到这种差异,即使DummyInteger类完成的工作最多与ArrayList执行的工作一样多。构造函数?是否与预编译的ArrayList代码有关?或者是影响处理时间的其他因素?

谢谢。

- 编辑 -

我认为比较两种不同类型的对象的问题会出现,但是,即使使用以下代码,与创建ArrayList相比也是如此:

class IntList {
    int [] elementData;

    public IntList() {
        elementData = new int [20];
    }
}

long start = System.nanoTime();
IntList n = new IntList();
long end = System.nanoTime();
long duration = end - start;
System.out.println(duration);

考虑到结果仍然相同,在这种情况下,由于执行了某些检查,创建ArrayList的开销应该更大,并且可以通过源代码。

还有一点需要注意的是,我在两个不同的运行中运行这两个代码,这消除了JVM初始化可能产生的任何开销。

4 个答案:

答案 0 :(得分:8)

  

为什么我们在运行时间中观察到这种差异?

因为您的测量没有意义:

  • 它是一个有缺陷的微基准标记(没有JVM预热,如果你只运行那么JVM启动可能也会干扰你的测量......)=> How do I write a microbenchmark?
  • nanotime的分辨率对于您正在测量的操作来说太低了 - 它们可能需要几纳秒的时间nanotime的分辨率接近1 ms

以下是我采用更强大的方法得到的结果:创建一个新的Integer需要大约6纳秒,而创建一个默认大小(10)的ArrayList在我的机器上大约需要19纳秒。

DummyInteger创建:

Run result "newInteger": 6.064 ±(95%) 0.101 ±(99%) 0.167 nsec/op
Run statistics "newInteger": min = 6.007, avg = 6.064, max = 6.200, stdev = 0.081
Run confidence intervals "newInteger": 95% [5.964, 6.165], 99% [5.897, 6.231]

创建列表:

Run result "newList": 19.139 ±(95%) 0.192 ±(99%) 0.318 nsec/op
Run statistics "newList": min = 18.866, avg = 19.139, max = 19.234, stdev = 0.155
Run confidence intervals "newList": 95% [18.948, 19.331], 99% [18.821, 19.458]

修改

我的"更强大的方法"也有一个缺陷,创造实际上被厚脸皮的JIT优化了......上面的新结果虽然结论相似:这些都是非常快的操作。

答案 1 :(得分:4)

加载课程是您可以做的最昂贵的事情之一。 (特别是对于没有做太多工作的类)许多内置类将在程序启动之前使用,因此不需要再次加载它们。当你使用该类时,它的代码就会变暖。

考虑以下示例,其中您提到的三个类是重复创建的

static class DummyInteger {
    private int i;

    public DummyInteger(int i) {
        this.i = i;
    }

    public int getI() {
        return i;
    }
}

static class IntList {
    int[] elementData;

    public IntList() {
        elementData = new int[20];
    }
}


public static void main(String... ignored) {
    timeEach("First time", 1);
    for (int i = 1000; i <= 5000; i += 1000)
        timeEach(i + " avg", i);
    for (int i = 10000; i <= 20000; i += 10000)
        timeEach(i + " avg", i);
}

public static void timeEach(String desc, int repeats) {
    long time1 = System.nanoTime();
    for (int i = 0; i < repeats; i++) {
        List l = new ArrayList();
    }
    long time2 = System.nanoTime();
    for (int i = 0; i < repeats; i++) {
        DummyInteger di = new DummyInteger(i);
    }
    long time3 = System.nanoTime();
    for (int i = 0; i < repeats; i++) {
        IntList il = new IntList();
    }
    long time4 = System.nanoTime();
    System.out.printf("%s: ArrayList %,d; DummyInteger %,d; IntList %,d%n",
            desc, (time2 - time1) / repeats, (time3 - time2) / repeats, (time4 - time3) / repeats);
}

使用Java 7更新21和-XX:+PrintCompilation

进行打印
     89    1             java.lang.String::hashCode (55 bytes)
     89    2             java.lang.String::charAt (29 bytes)
First time: ArrayList 41,463; DummyInteger 422,837; IntList 334,986
1000 avg: ArrayList 268; DummyInteger 60; IntList 136
    120    3             java.lang.Object::<init> (1 bytes)
2000 avg: ArrayList 321; DummyInteger 75; IntList 142
3000 avg: ArrayList 293; DummyInteger 63; IntList 133
    123    4             Main::timeEach (152 bytes)
    124    5             java.util.AbstractCollection::<init> (5 bytes)
    124    6             java.util.AbstractList::<init> (10 bytes)
    125    7             java.util.ArrayList::<init> (44 bytes)
4000 avg: ArrayList 309; DummyInteger 64; IntList 175
    126    8             java.util.ArrayList::<init> (7 bytes)
    127    9             Main$DummyInteger::<init> (10 bytes)
    127   10             Main$IntList::<init> (13 bytes)
5000 avg: ArrayList 162; DummyInteger 70; IntList 149
10000 avg: ArrayList 0; DummyInteger 0; IntList 0
20000 avg: ArrayList 0; DummyInteger 0; IntList 0

您可以看到性能提升ArrayList是最慢的。最后,JIT确定不使用对象,不需要创建。然后循环为空,不需要运行,因此平均时间下降到0。

答案 2 :(得分:1)

  

现在,我的问题是,为什么我们在跑步中观察到这种差异   时间,即使DummyInteger类完成的工作似乎是   最多与ArrayList构造函数执行的一样多?

因为这个基准很糟糕。你不能在1次跑步中说什么。

  

是否与预编译的ArrayList代码有关?

没有

答案 3 :(得分:0)

您正在比较两种不同类型对象的时间性能,即arraylist和Your Dummyobject,这就是时间统计数据不同的原因。现在让我们来讨论你的问题

  

创建预先实现的java类的对象要快得多   创建自定义对象?

它不是正确的声明,因为预先实现的java也是一种自定义对象,但是由其他人为您创建(就像您自己创建自定义对象一样)。所以不是 预先实现的java类vs自定义对象,但实际上取决于在对象创建期间发生的操作。