mildside has asked for the wisdom of the Perl Monks concerning the following question:

How clever is the Perl interpreter? Is there any advantage in doing this:
return wantarray ? @my_array : scalar @my_array;
as opposed to just:
return @my_array;
when the sub is called in a scalar context?

If an array is returned from a sub in a scalar context, does the conversion to a scalar happen before returning (thus returning a scalar) or after (thus returning an array and converting to scalar)?

I would have guessed that Perl was smart enough to do the former, but I have seen the simple wantarray code above used a few times and I'm wondering if it is really necessary.

Thanks in advance, and Cheers!

Replies are listed 'Best First'.
Re: returning arrays from a sub - how clever is Perl?
by pg (Canon) on Oct 21, 2003 at 23:42 UTC

    Is Perl clever enough? I think Perl is stupid enough to return the entire array first and then scalar it. Don't underestimate Perl and the guys who created it ;-P

    Is it neccessary? This is really a question for you, not others. Is it not all depends on your reqirement?

    By the way, to improve performance, you better return array ref instead of array. (I trust you that by doing scalar @array, what you want is number of elements, not array ref)

    Some demo:
    use strict; use warnings; { my @a = sub1(); print join(",", @a), "\n"; my $a = sub1(); print $a, "\n";; } { my @a = sub2(); print join(",", @a), "\n"; my $a = sub2(); print $a, "\n"; print join(",", @$a), "\n"; } sub sub1 { my @my_array = (1,11,111,1111); return wantarray ? @my_array : scalar @my_array; } sub sub2 { my @my_array = (2,22,222,2222); return wantarray ? @my_array : \@my_array; }
      Yes, as pg has pointed out, the main difference would be in the performance as return @my_array; will create a copy of the array, and this isn't efficient if all you want is just the number of element in the array. The performance difference will be significant if the size of the array is large.

      Personally, I always try to return the reference to array with return \@my_array; unless the size of the array is known to be very small.

        "return @my_array; will create a copy of the array, and this isn't efficient if all you want is just the number of element in the array."

        I don't believe it's correct that a copy of @my_array is made when return @my_array is evaluated in scalar context. Perl is smart enough to know about the context from within the subroutine. Context passes through, so to speak. No copy is made, and all that happens is that @my_array happily tells return to tell the lvalue scalar that there are 23 elements (or whatever number).

        Here is Yet Another Benchmark. I believe that this gets right down to brass tacks. All that we are comparing is how fast return @array takes place in scalar context, versus return $nonarray, also in scalar context. Thus, we can see if there is extra time being taken to create a copy of @array. I think you'll find the results surprising.

        First, the code:

        use strict; use warnings; use Benchmark; my @array; push @array, "Test string" for 1..20000; my $nonarray = "Test string"; sub passarray { return @array; } sub passscalar { return $nonarray; } my $count = 500000; timethese ( $count, { "Scalar" => sub { my $test = passscalar() }, "List" => sub { my $test = passarray() } } );

        And now for the exciting results...

        Benchmark: timing 500000 iterations of List, Scalar... List: 0 wallclock secs ( 0.73 usr + 0.00 sys = 0.73 CPU) @ 6830 +60.11/s (n=500000) Scalar: 2 wallclock secs ( 1.38 usr + 0.00 sys = 1.38 CPU) @ 3617 +94.50/s (n=500000)

        Surprised to find that the return @array; subroutine was faster than the return $nonarray;? I was. I figued that they would be about equal. There couldn't possibly be a copying of the array going on; that would take way too long. That part doesn't surprise me. What surprises me is that it's quicker to say, "I have 20,000 elements" than it is to say, "I contain the string, 'Test string'". By the way, the same time differential holds true even if $nonarray contains just a one-character string.

        The explanation that I can come up with is pure speculation: When comparing the operation of an array knowing its size, compared to the operation of a scalar knowing its value, the former is a less costly operation.


        Dave


        "If I had my life to do over again, I'd be a plumber." -- Albert Einstein
Re: returning arrays from a sub - how clever is Perl?
by davido (Cardinal) on Oct 22, 2003 at 00:33 UTC
    Your first snippet just explicitly conveys some logic that otherwise must go on behind the scenes.

    I was curious as to the performance hit of invoking wantarray and scalar along with a trinary operator, as opposed to allowing the internals to take care of the decision. So I wrote a sample test that creates an array of 20,000 elements (by preextending an empty array by assigning a value to $#array).

    I then wrote two functions; one with your wantarray logic and one without, put them side by side, and wrote wrapper functions for them that invoked each function twice; once in scalar and once in list context. I then used Benchmark to test.

    The results were seemingly inconclusive even for such a large array, and going through 5000 iterations of the test. But then I realized that I was testing more the speed of returning an array than the speed of testing the logic. So I changed it to 50,000 iterations on an array of 1000 elements. ...and the results were... inconclusive. Or at least, the results indicated that there is no significant difference between the first and the second construct.

    Update: hardburn points out that there is a flaw in my test. However I had already refined it to what I believe to be a better comparison. See the discussion in this node for the more accurate test.


    Dave


    "If I had my life to do over again, I'd be a plumber." -- Albert Einstein

      I thought your benchmark was pretty good, but I thought it'd be interesting to see the performance of each in scalar and array context by themselves. So without further ado, here is the benchmark code I used:

      And here are the results:

      To me these are interesting for several reasons. They are fairly consistent with your results -- for the array context, at least. They also show the version without explicit context check to be faster in scalar context. Note the extremely high rate makes the results somewhat useless, but interesting nonetheless.

        ... show the version without explicit context check to be faster in scalar context
        Ahhh! So maybe Perl is clever after all. I thought that it should be.

        Cheers!

      sub test_with { my @testary = with_want(); my $testscalar = with_want(); } sub test_without { my @testary = with_want(); my $testscalar = with_want(); }

      I'd expect the results to be similar, since you're testing the exact same thing! I think you want that second one to be:

      sub test_without { my @testary = without(); my $testscalar = without(); }

      ----
      I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
      -- Schemer

      :(){ :|:&};:

      Note: All code is untested, unless otherwise stated

Re: returning arrays from a sub - how clever is Perl?
by mildside (Friar) on Oct 22, 2003 at 00:26 UTC
    So from the replies I have so far, it seems that Perl is not clever enough to optimize this case. I think this optimization would have been quite easy to implement: since you know the context for the return value at the time of calling the sub, why return an array when you know a scalar is required?

    Cheers!

    Update: After a few more replies with some benchmarking evidence (thanks davido and revdiablo) it appears my original guess was correct and that Perl is clever after all. Phew! All is right with the world.