System.arrayCopy Vs array clone

M

mei

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.
 
D

Daniel Pitts

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. 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.
 
?

=?ISO-8859-1?Q?Arne_Vajh=F8j?=

mei said:
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?

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
 
L

Lew

Daniel said:
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
 
M

mei

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
 
M

mei

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.
 
L

Lew

mei said:
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
 
E

Eric Sosman

Arne Vajhøj wrote On 02/27/07 22:41,:
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

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.
 
M

mei

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.
 
?

=?ISO-8859-1?Q?Arne_Vajh=F8j?=

Eric said:
Arne Vajhøj wrote On 02/27/07 22:41,:
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

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.

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
 
L

Lew

Arne said:
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
 
C

Chris Uppal

Lew said:
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));
}
}
================================
 
G

Guest

Lew said:
Arne said:
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?

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
 
?

=?ISO-8859-1?Q?Arne_Vajh=F8j?=

Chris said:
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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top