0
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

现在,我的问题是,为什么我们会在运行时间上观察到这种差异,即使DummyIntegerClass 所做的工作似乎最多与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

4 回答 4

8

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

因为您的测量毫无意义:

  • 这是一个有缺陷的微基准(没有 JVM 预热,如果你只运行它,那么 JVM 启动可能也会影响你的测量......)=>我如何编写微基准?
  • 的分辨率nanotime对于您正在测量的操作来说太低了——它们可能需要几纳秒nanotime的时间,而分辨率接近 1 毫秒

以下是我遵循更强大的方法得到的结果:创建一个新的 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 优化掉了……上面的新结果虽然结论是相似的:这些都是非常快速的操作。

于 2013-05-18T08:09:16.340 回答
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 update 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。

于 2013-05-18T08:26:36.883 回答
1

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

因为这个基准很糟糕。您无法从 1 次运行中看出什么。

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

不。

于 2013-05-18T08:09:30.060 回答
0

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

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

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

于 2013-05-18T07:55:19.023 回答