Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Re: Deepcopy of complex structures. (resolved)

by BrowserUk (Patriarch)
on Jan 21, 2003 at 12:53 UTC ( [id://228666]=note: print w/replies, xml ) Need Help??


in reply to Deepcopy of complex structures.

My thanks to iguanodon for the reference and install trace for Clone and Jeffa for the quickstart on its use. Special thanks to Merlyn for the reference to his article which told me what to look for in testing, and to everyone else for their help.

As you can see from the Benchmark below, not all deepcopy methods are equal in terms of performance. (Data::Dumper isn't tested because I couldn't work out how to use it for this. See comments in code below).

C:\test>228543 Benchmark: running CloneCopy, DeepCopy, StorableCopy , each for at least 1 CPU seconds ... CloneCopy: 1 wallclock secs ( 1.10 usr + 0.01 sys = 1.11 CPU) @ +27625.00/s (n=30719) DeepCopy: 1 wallclock secs ( 1.10 usr + 0.00 sys = 1.10 CPU) @ +20539.93/s (n=22635) StorableCopy: 1 wallclock secs ( 1.03 usr + 0.00 sys = 1.03 CPU) @ +541.67/s (n=559) Rate StorableCopy DeepCopy CloneCopy StorableCopy 542/s -- -97% -98% DeepCopy 20540/s 3692% -- -26% CloneCopy 27625/s 5000% 34% -- Difference found!

As you can see, Clone wins hands down, with my own (modified from the original) attempt coming a deceptively creditable second.

What I mean by deceptively, is that despite it's relatively tardy performance, Storable really takes second place honours because, as is indicated by the "Differences found!" output at the bottom, not all the routines do the same thing. As was pointed out above by ihb, my DeepCopy routine does handle recursive structures. The difference is clearly shown in the side-by-side Dumper output below. A second flaw is that it doesn't handle bless'd references at all. I haven't tested the others for this as it isn't a requirement for my purposes.

Data::Dumper comparison of cloned structures

$DeepCopy = { $CloneCopy = { $Stor +ableCopy = { 'rrs1' => [ 'rrs1' => [ 'rr +s1' => [ \\1, \\1, + \\1, \\2, \\2, + \\2, \\3, \\3, + \\3, \\4, \\4, + \\4, \\5 \\5 + \\5 ], ], +], 'rrs2' => [ 'rrs2' => [ 'rr +s2' => [ $VAR1->{'rrs1'}[0], $VAR2->{'rrs1'}[0], + $VAR3->{'rrs1'}[0], $VAR1->{'rrs1'}[1], $VAR2->{'rrs1'}[1], + $VAR3->{'rrs1'}[1], $VAR1->{'rrs1'}[2], $VAR2->{'rrs1'}[2], + $VAR3->{'rrs1'}[2], $VAR1->{'rrs1'}[3], $VAR2->{'rrs1'}[3], + $VAR3->{'rrs1'}[3], $VAR1->{'rrs1'}[4] $VAR2->{'rrs1'}[4] + $VAR3->{'rrs1'}[4] ], ], ] +, 'h1' => { 'h1' => { 'h1 +' => { 'A' => 1, 'A' => 1, + 'A' => 1, 'B' => 2, 'B' => 2, + 'B' => 2, 'C' => 3, 'C' => 3, + 'C' => 3, 'D' => 4, 'D' => 4, + 'D' => 4, 'E' => 5 'E' => 5 + 'E' => 5 }, }, } +, 'rs' => [ 'rs' => [ 'rs +' => [ ${$VAR1->{'rrs1'}[0]}, ${$VAR2->{'rrs1'}[0]}, + ${$VAR3->{'rrs1'}[0]}, ${$VAR1->{'rrs1'}[1]}, ${$VAR2->{'rrs1'}[1]}, + ${$VAR3->{'rrs1'}[1]}, ${$VAR1->{'rrs1'}[2]}, ${$VAR2->{'rrs1'}[2]}, + ${$VAR3->{'rrs1'}[2]}, ${$VAR1->{'rrs1'}[3]}, ${$VAR2->{'rrs1'}[3]}, + ${$VAR3->{'rrs1'}[3]}, ${$VAR1->{'rrs1'}[4]} ${$VAR2->{'rrs1'}[4]} + ${$VAR3->{'rrs1'}[4]} ], ], ] +, 'self' => [ 'self' => $VAR2->{'rs'} 'se +lf' => $VAR3->{'rs'} ${$VAR1->{'rrs1'}[0]} };, }; ${$VAR1->{'rrs1'}[1]}, ${$VAR1->{'rrs1'}[2]}, ${$VAR1->{'rrs1'}[3]}, ${$VAR1->{'rrs1'}[4]} ] };

The benchmark code

#! perl -slw use strict; use Clone qw[clone]; use Data::Dumper; use Storable qw[dclone freeze]; use Benchmark qw[cmpthese]; sub deepcopy{ return $_[0] unless ref $_[0]; return [ map{ deepcopy($_) } @{$_[0]} ] if ref $_[0] eq 'ARRAY +'; return { map{ deepcopy($_) } %{$_[0]} } if ref $_[0] eq 'HASH' +; return \do{ ${$_[0]} }; } sub factorial { my ($f,$n) = (1,shift); $f *= $n-- while( $n ); $f; } sub multicheck { my $check = factorial($#_); my ($i, @ok) = (0, (0) x @_); while (@_) { my $first = shift; for my $next (@_) { $ok[$i] += ($first eq $next); } $i++; } my $count = do{ local $a; $a += $_ for @ok; $a; }; return $count == $check; } my @rs = \do{1..5}; my @rrs1 = \(@rs); my @rrs2 = \(@rs); my %h1 = do{ my $key='A'; map{ $key++ => $_} 1..5; }; my %hash = ( rs=> \@rs, rrs1=> \@rrs1, rrs2=> \@rrs2, h1=>\%h1 ); $hash{self} = $hash{rs}; my $DeepCopy = deepcopy( \%hash ); my $CloneCopy = clone( \%hash ); my $StorableCopy= dclone( \%hash ); #print Dumper [\%hash, $CloneCopy, $StorableCopy, $DeepCopy]; =pod Comment (moan) The following code should, according to my reading of the Data::Dumper + pod, result in $DDumperCopy becoming a ref to a copy of %hash,, but it doesn't? :( my $DDumperCopy; $Data::Dumper::Purity = 1; $DDumperCopy = eval( Dumper(\%hash) ) and warn $@ if $@; print Dumper $DDumperCopy; =cut cmpthese( -1, { DeepCopy => '$DeepCopy = deepcopy( \%hash );', CloneCopy => '$CloneCopy = clone( \$hash );', StorableCopy=> '$StorableCopy = dclone( \%hash );', # DDumperCopy => '', }); my $strDeepCopy = freeze( $DeepCopy ); my $strCloneCopy = freeze( $CloneCopy ); my $strStorableCopy = freeze( $StorableCopy ); print "\nDifferences found!" unless multicheck( $strDeepCopy, $strCloneCopy, $strStorableCo +py ); #print Dumper $DeepCopy, $CloneCopy, $StorableCopy # unless multicheck( $strDeepCopy, $strCloneCopy, $strStorableCo +py );

BTW: If anyone has a (reference to a) better version of my multicheck() routine, preferably one that will tell me which of the things passed differed if only one does, I'd love to see it.


Examine what is said, not who speaks.

The 7th Rule of perl club is -- pearl clubs are easily damaged. Use a diamond club instead.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://228666]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others cooling their heels in the Monastery: (1)
As of 2024-04-25 00:18 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found