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);

The previous code produces the following output:

341000

Whereas:

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

produces the following output:

17000

Now, my question is, why do we observe such difference in the running time, even though the work done by the DummyInteger Class seems to be at most as much as that performed by the ArrayList constructor? Does it have to do with ArrayList's code being precompiled? or is it some other factor that's affecting the processing time?

Thank you.

--EDIT--

I thought the issue of comparing two different types of objects would arise, however, even with the following code, compared to creating an ArrayList that is:

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);

The result is still the same, bearing in mind, that in this case, the overhead by the creation of the ArrayList should be greater, due to certain checks that are performed, and could be found by going through the source code.

One more thing to note, is that, I run both codes on two different runs, which eliminates any overhead that may result from the initialisation of the JVM.

BitRiver
  • 291
  • 3
  • 11
  • 3
    maybe because you are comparing apples with potatoes? – guido May 18 '13 at 07:53
  • I feel its a wrong bench-mark, for me it took `4516 and 20526` respectively. I think when you start your program there is some overhead, which JVM takes to initialize and thats why the delay – sanbhat May 18 '13 at 07:54
  • 1
    There's no such thing as *precompiled*. Also, in my machine it runs `~8000` and `~60000`. – acdcjunior May 18 '13 at 07:55
  • @acdcjunior none the less, the difference is evident. We're talking of a difference of almost 8 magnitudes. – BitRiver May 18 '13 at 08:07
  • @sanbhat I feel it's normal if it takes different running times on different machines, however, the difference is still evident. Also, kindly check my edit concerning the initialisation of the JVM. – BitRiver May 18 '13 at 08:08
  • Or rather, there's no such thing as *not* precompiled. – user207421 May 18 '13 at 08:31

4 Answers4

8

why do we observe such difference in the running time?

Because your measurement is meaningless:

  • it is a flawed microbenchmark (no JVM warmup, if you only run that then the JVM startup probably intereferes with your measurement too...) => How do I write a microbenchmark?
  • the resolution of nanotime is WAY too low for the operations your are measuring - they probably take a few nanoseconds wheeas nanotime's resolution is closer to 1 ms

Here are the results I get following a more robust methodology: creating a new Integer takes around 6 nanoseconds, while creating an ArrayList of default size (10) takes about 19 nanoseconds on my machine.

DummyInteger creation:

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]

List creation:

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]

EDIT

My "more robust methodology" also had a flaw and the creation was actually optimised away by the cheeky JIT... New results above although the conclusion is similar: these are extremely fast operations.

Community
  • 1
  • 1
assylias
  • 321,522
  • 82
  • 660
  • 783
4

Loading a class is one of the most expensive things you can do. (Especially for a class which doesn't do much) Many of the built in classes will be used before your program starts and so they don't need to be loaded again. As you use the class more the code for it is warmed up.

Consider the following example where the three class you mentioned are create repeatedly

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);
}

prints with Java 7 update 21 and -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

You can see as the performance improves ArrayList is the slowest. Finally the JIT determines the objects are not used and don't need to be created. Then the loop is empty and doesn't need to be run so the average time drops to 0.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
1

Now, my question is, why do we observe such difference in the running time, even though the work done by the DummyInteger Class seems to be at most as much as that performed by the ArrayList constructor?

Because this benchmark is bad. You can't tell nothing from 1 run.

Does it have to do with ArrayList's code being precompiled?

No.

Adam Stelmaszczyk
  • 19,665
  • 4
  • 70
  • 110
0

you are comparing the time performance for two different types of objects i.e arraylist and Your Dummyobject thats why time stats are different. Now lets come to your question

Creating an object of a pre-implemented java class is much faster than creating a custom object?

its not the right statement as pre-implemented java is also a kind of custom object but created by somebody else for you (like you create the custom object for yourself). So its not pre-implemented java class vs custom object but actually depends on what are the operation that takes place during object creation.

M Sach
  • 33,416
  • 76
  • 221
  • 314