Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Most efficient way to swap two array values in Perl

Tags:

perl

I was curious what the most efficient way to swap two elements in an array in Perl would be.

Say I want to switch the elements with indexes of 3 and 5 respectively, what would be the fastest way to do that?

My thoughts were something like:

@array[3,5] = @array[5,3];

Is this very efficient? My hope is that it wouldn't make a difference if those two array values contained large amounts of data. I would expect the back-end to change the pointers around, without doing any actual copying. Is that correct?

like image 961
GoldenNewby Avatar asked Dec 12 '22 04:12

GoldenNewby


2 Answers

What do you mean "large amounts of data"? If you mean references to large data structures, it doesn't matter how you do it; it will be crazy fast. (Less than one nanosecond per swap.) So I'm going to assume you meant "large strings".


Before 5.20, the code you posted would copy the strings. You can avoid that using Data::Alias.

use Data::Alias;
alias @array[3,5] = @array[5,3];

Or you can do the same with a little XS if you want to avoid the syntax magic of Data::Alias:

void swap_array_elements(AV * av, IV ix1, IV ix2) {
   SV ** p_ele1 = av_fetch(av, ix1, 1);
   SV ** p_ele2 = av_fetch(av, ix2, 1);
   SV * sv = *p_ele1;
   *p_ele1 = *p_ele2;
   *p_ele2 = sv;
}

Since 5.20, the string copies were replaced with a simple pointer swap.

$ 5.18t/bin/perl -MDevel::Peek -e'
   my @a = qw( a b c d e f );
   Dump($a[3]);
   Dump($a[5]);
   @a[3,5] = @a[5,3];
   Dump($a[3]);
   Dump($a[5]);
' 2>&1 | grep '^  PV'
  PV = 0x353fba0 "d"\0
  PV = 0x357abb0 "f"\0
  PV = 0x3541ff0 "f"\0
  PV = 0x3537940 "d"\0

$ 5.20t/bin/perl -MDevel::Peek -e'
   my @a = qw( a b c d e f );
   Dump($a[3]);
   Dump($a[5]);
   @a[3,5] = @a[5,3];
   Dump($a[3]);
   Dump($a[5]);
' 2>&1 | grep '^  PV'
  PV = 0xe9ace0 "d"\0
  PV = 0xe8f230 "f"\0
  PV = 0xe8f230 "f"\0
  PV = 0xe9ace0 "d"\0

This means that the straightforward @array[3,5] = @array[5,3]; will do just fine.

like image 136
ikegami Avatar answered Jan 07 '23 02:01

ikegami


Running bvr's benchmark script with a modification:

my @array = ('thing' x 1E5, 'stuff' x 5E5, 'that' x 1E6, 'joe' x 5E6,
             'sofa' x 1E7, 'jim' x 5E7);

Results:

           Rate      slice       temp      alias
slice    1.98/s         --       -69%      -100%
temp     6.47/s       227%         --      -100%
alias 4218715/s 213045016%  65227727%         --
like image 24
mob Avatar answered Jan 07 '23 03:01

mob