Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

System.arrayCopy Vs array clone

179 views
Skip to first unread message

mei

unread,
Feb 27, 2007, 3:52:41 PM2/27/07
to
Hello,

I read in bloch's effective java that the only interesting use of
clone() is to copy array cheaply. On the web, I found opposite opinions:
Some people pretend that instantiate a new array, then call
System.arrayCopy is faster.
I made a quick bench that seems to confirm this second hypothesis.
However, with all the respect I have for the author, I wished all the
same to ask the question on this newsgroup. Is there something I and a
few other people from the Internet missed?
Thanks in advance,
Mei.

Daniel Pitts

unread,
Feb 27, 2007, 4:22:47 PM2/27/07
to

Its probably an "out of date" assertion. True when written, but false
now. You have to remember that Java has gone through a lot of
optimizations through the years. Its quite possible that "new
int[100]" and System.arrayCopy() have been optimized, and
array.clone() has not.

You may noticed that in Java 1.6, there is a new way to "copy"
arrays.
<http://java.sun.com/javase/6/docs/api/java/util/
Arrays.html#copyOf(T[],%20int)>, making clone obsolete, and limiting
the need for explicit arrayCopy calls.

Arne Vajhøj

unread,
Feb 27, 2007, 10:41:35 PM2/27/07
to

It probably depend on Java vendor, platform and version.

Meaning that there are a few hundred possible results.

There are absolutely no guarantee that IBM Java 1.3.1 on AIX PPC
will have same characteristics as SUN Java 1.5.0 on Win32 x86.

That said then my expectation will be that:
- the difference is small because they basically do the same thing
- extremely few real world applications will be effected by the
difference

Arne

Lew

unread,
Feb 27, 2007, 11:07:56 PM2/27/07
to
Daniel Pitts wrote:
> On Feb 27, 12:52 pm, mei <m...@you.fr> wrote:
>> Hello,
>>
>> I read in bloch's effective java that the only interesting use of
>> clone() is to copy array cheaply. On the web, I found opposite opinions:
>> Some people pretend that instantiate a new array, then call
>> System.arrayCopy is faster.
>> I made a quick bench that seems to confirm this second hypothesis.
>> However, with all the respect I have for the author, I wished all the
>> same to ask the question on this newsgroup. Is there something I and a
>> few other people from the Internet missed?
>> Thanks in advance,
>> Mei.
>
> Its probably an "out of date" assertion.

No, it remains interesting. Notice that Bloch doesn't say "recommended" use.

There have been a number of threads here touching on the dangers of assuming
performance characteristics in a Java program, especially between versions.
Algorithmic characteristics of a benchmark and execution profiles from its
testbed both influence the outcome.

Since you actually measured the performance you have some facts about
System.arrayCopy(). Would you be willing to share your benchmark code and how
you ran it?

- Lew

mei

unread,
Feb 28, 2007, 1:50:19 PM2/28/07
to
Hello Lew,

Lew a écrit :


>
> No, it remains interesting. Notice that Bloch doesn't say "recommended"
> use.

I don't understand. What do you mean?

> [...]


>
> Since you actually measured the performance you have some facts about
> System.arrayCopy(). Would you be willing to share your benchmark code
> and how you ran it?

Hereunder is the benchmark code I wrote. Since I am not a specialist for
writing such kind of code, if you detect any flaws, please let me know.
I used java hotspot 1.5.0_10

public class Bench {
private static final int[] tableau = {1, 2, 3, 4, 5, 6, 7, 8, 9};
private static final int count = 1000000;

static void testCopy() {
long startTime = System.currentTimeMillis();
long res = 0;
for (int i = 0; i < count; i++) {
int[] copy = new int[tableau.length];
System.arraycopy(tableau, 0, copy, 0, tableau.length);
for (int j = 0; j < copy.length; j++)
res += copy[j];
}
long endTime = System.currentTimeMillis();
System.out.println("testCopy(): dummy result=" + res + "
time=" + (endTime - startTime));
}

static void testClone() {
long startTime = System.currentTimeMillis();
long res = 0;
for (int i = 0; i < count; i++) {
int[] copy = tableau.clone();
for (int j = 0; j < copy.length; j++)
res += copy[j];
}
long endTime = System.currentTimeMillis();
System.out.println("testClone(): dummy result=" + res + "
time=" + (endTime - startTime));
}

public static void main(String[] args) {
testCopy();
testClone();

testCopy();
testClone();

testCopy();
testClone();
}
}

Here's a typical output:

testCopy(): dummy result=45000000 time=140
testClone(): dummy result=45000000 time=438
testCopy(): dummy result=45000000 time=125
testClone(): dummy result=45000000 time=437
testCopy(): dummy result=45000000 time=125
testClone(): dummy result=45000000 time=454

mei

unread,
Feb 28, 2007, 1:58:40 PM2/28/07
to
Hello Arne,

Arne Vajhøj a écrit :


> It probably depend on Java vendor, platform and version.
>
> Meaning that there are a few hundred possible results.
>
> There are absolutely no guarantee that IBM Java 1.3.1 on AIX PPC
> will have same characteristics as SUN Java 1.5.0 on Win32 x86.

True. I'm using Java 1.5 and win32 x86.
J. Bloch were using Java 1.3 and win32 x86.
However he didn't say explicitly that he ran such benchmark by himself
for this matter. His expression was: "Because of its many shortcomings,
some expert programmers simply choose never to override the clone method
and never to invoke it except, perhaps, to copy arrays cheaply".

> That said then my expectation will be that:
> - the difference is small because they basically do the same thing

Hmmm... Clone() entails the navigation through the inheritance hierarchy
to finally invoke Object.clone(), then by a mechanism I ignore the
details provide an instance of the correct type.

Lew

unread,
Mar 1, 2007, 1:03:29 AM3/1/07
to
mei wrote:
> Hmmm... Clone() entails the navigation through the inheritance hierarchy
> to finally invoke Object.clone(), then by a mechanism I ignore the
> details provide an instance of the correct type.

That doesn't happen at run time. The class that inherits Object has a direct
dispatch to its own inherited methods; it doesn't need to do some sort of
hierarchical search at runtime for a class that implements the method.

-- Lew

Eric Sosman

unread,
Mar 1, 2007, 10:57:33 AM3/1/07
to
Arne Vajhøj wrote On 02/27/07 22:41,:

My expectations were a lot like yours, but I ran some
timing tests and was surprised. On my vendor/platform/version
combination, duplicating an int[N] array takes

0.035*N + 2.44 microseconds for clone()
0.020*N + 0.38 microseconds for new + arrayCopy
0.045*N - 0.28 microseconds for new + for-loop

(These are straight-line regressions for values of N in
{0,1,10,100,1000,2500,5000,7500,10000}, with the number
of repetitions at each N adjusted to make the test take
roughly four seconds. The sign of the constant term in
the third method suggests that copying lots and lots of
zero-length arrays with new-plus-loop could make your
program arbitrarily fast, but I wouldn't place a whole
lot of faith in that conclusion ...)

On my configuration, then, new-plus-arrayCopy is the
fastest, cloning takes 75% longer, and new-plus-loop takes
125% longer.

> - extremely few real world applications will be effected by the
> difference

Agreed; these timing tests have little practical use.

--
Eric....@sun.com

mei

unread,
Mar 1, 2007, 1:55:45 PM3/1/07
to
Lew a écrit :

> That doesn't happen at run time. The class that inherits Object has a
> direct dispatch to its own inherited methods; it doesn't need to do some
> sort of hierarchical search at runtime for a class that implements the
> method.

Ok. Thank you.

Arne Vajhøj

unread,
Mar 1, 2007, 10:26:33 PM3/1/07
to

I tried with a test also.

Results are below.

They seem rather blurred to me.

Arne

================================================

C:\>type CloneSpeed.java
public class CloneSpeed {
private final static int N = 10000;
private final static int REP = 100000;


public static void main(String[] args) {

int[] a = new int[N];
long t1 = System.currentTimeMillis();
for(int i = 0; i < REP; i++) {
int[] b = (int[])a.clone();
}
long t2 = System.currentTimeMillis();
System.out.println("clone: " + (t2-t1));
long t3 = System.currentTimeMillis();
for(int i = 0; i < REP; i++) {
int[] b = new int[a.length];
System.arraycopy(a, 0, b, 0, a.length);
}
long t4 = System.currentTimeMillis();
System.out.println("arraycopy: " + (t4-t3));
}
}

C:\>java -version
java version "1.5.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-b64)
Java HotSpot(TM) Client VM (build 1.5.0-b64, mixed mode)

C:\>javac CloneSpeed.java

C:\>java CloneSpeed
clone: 2641
arraycopy: 2453

C:\>java CloneSpeed
clone: 2969
arraycopy: 2453

C:\>java CloneSpeed
clone: 2609
arraycopy: 2469

C:\>java -server CloneSpeed
clone: 3282
arraycopy: 3140

C:\>java -server CloneSpeed
clone: 3297
arraycopy: 3140

C:\>java -server CloneSpeed
clone: 3281
arraycopy: 3141

C:\>java -version
java version "1.6.0"
Java(TM) SE Runtime Environment (build 1.6.0-b105)
Java HotSpot(TM) Client VM (build 1.6.0-b105, mixed mode, sharing)

C:\>javac CloneSpeed.java

C:\>java CloneSpeed
clone: 2844
arraycopy: 3016

C:\>java CloneSpeed
clone: 2843
arraycopy: 3000

C:\>java CloneSpeed
clone: 2875
arraycopy: 3031

C:\>java -server CloneSpeed
clone: 3281
arraycopy: 3140

C:\>java -server CloneSpeed
clone: 3312
arraycopy: 3328

C:\>java -server CloneSpeed
clone: 3265
arraycopy: 3188

Lew

unread,
Mar 3, 2007, 9:20:31 AM3/3/07
to
Arne Vajhøj wrote:
> C:\>type CloneSpeed.java
> public class CloneSpeed {
> private final static int N = 10000;
> private final static int REP = 100000;
> public static void main(String[] args) {
> int[] a = new int[N];
> long t1 = System.currentTimeMillis();
> for(int i = 0; i < REP; i++) {
> int[] b = (int[])a.clone();
> }
> long t2 = System.currentTimeMillis();
> System.out.println("clone: " + (t2-t1));
> long t3 = System.currentTimeMillis();
> for(int i = 0; i < REP; i++) {
> int[] b = new int[a.length];
> System.arraycopy(a, 0, b, 0, a.length);
> }
> long t4 = System.currentTimeMillis();
> System.out.println("arraycopy: " + (t4-t3));
> }
> }

For tests like this, would it make sense to run the loop a bunch of times
before measuring the speed in order to let the Hotspot mechanism settle down?

-- Lew

Chris Uppal

unread,
Mar 3, 2007, 3:38:20 PM3/3/07
to
Lew wrote:

> For tests like this, would it make sense to run the loop a bunch of times
> before measuring the speed in order to let the Hotspot mechanism settle
> down?

It normally would be /much/ better, but in this specific case I don't think it
makes a lot of difference. One reason is that each loop of the test is quite
long (longer than I would normally use myself); the other is that both clone()
and arrayCopy() end up in precompiled (not JITed) code for the bulk of their
execution time, so there's not much for the JITer to do. It would probably
have made a more significant difference if we were including hand-written loop
in the benchmark (as in "mie"'s code).

Still, and for what it's worth, I wrapped a loop around the test. Code follow
at the end.

With SIZE = 10,000 and LOOPS = 100,000, I didn't find a lot of difference
beween copy() and new()+arrayCopy() using a 1.5 or 1.6, client or server, JVM
(there were small differences but not enough to be worth discussing). On the
other hand, with a much smaller array, such as "mei" used, SIZE = 10 and LOOPS
= 10,000,000, there were very considerable differences, with clone() typically
about 3 or 4 times slower (depending on which JVM I used). But then, the time
taken to copy a small array is much less likely to have a significant effect on
the overal runtime of a program.

I suspect there may be a test somewhere in the implementation of clone() which
switches to a faster implementation for arrays over a certain size.

Incidentally, one consistent feature of the numbers is that the 1.5 JVM was a
bit faster than the 1.6 for this benchmark...

-- chris


================================
public class Test
{
private final static int SIZE = 10 * 1000;
private final static int LOOPS = 100 * 1000;


public static void
main(String[] args)
{

System.out.println("Clone()\t\tNew()+ArrayCopy()");
int[] a = new int[SIZE];
for (;;)
timeIt(a);
}

private static void
timeIt(int[] a)
{
long start = System.currentTimeMillis();
for(int i = 0; i < LOOPS; i++)


{
int[] b = (int[])a.clone();
}

long end = System.currentTimeMillis();
System.out.print((end-start) + "\t\t");

start = System.currentTimeMillis();
for(int i = 0; i < LOOPS; i++)


{
int[] b = new int[a.length];
System.arraycopy(a, 0, b, 0, a.length);
}

end = System.currentTimeMillis();
System.out.println((end-start));
}
}
================================


Arne Vajhøj

unread,
Mar 3, 2007, 9:52:23 PM3/3/07
to

Maybe.

But if the non-optimized number of iterations is significant
in 100000 repetitions, then I would say that the non-optimized
results are probably more relevant than the optimized one.

Arne

Arne Vajhøj

unread,
Mar 3, 2007, 10:00:01 PM3/3/07
to
Chris Uppal wrote:
> With SIZE = 10,000 and LOOPS = 100,000, I didn't find a lot of difference
> beween copy() and new()+arrayCopy() using a 1.5 or 1.6, client or server, JVM
> (there were small differences but not enough to be worth discussing). On the
> other hand, with a much smaller array, such as "mei" used, SIZE = 10 and LOOPS
> = 10,000,000, there were very considerable differences, with clone() typically
> about 3 or 4 times slower (depending on which JVM I used). But then, the time
> taken to copy a small array is much less likely to have a significant effect on
> the overal runtime of a program.

A completeley non scientific explanation build on gut feeling
is that clone has more runtime overhead to verify that the
object is cloneable and that the assignment should not give
a class cast exception than arraycopy use to verify that
src and dst are the same type.

> Incidentally, one consistent feature of the numbers is that the 1.5 JVM was a
> bit faster than the 1.6 for this benchmark...

A new version can not fe faster at everything. I belive that 1.6 in
general are sligtly faster than 1.5.

Arne

0 new messages