This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Partially pessimise in-place sorting
authorDavid Mitchell <davem@iabyn.com>
Wed, 10 Aug 2016 14:12:56 +0000 (15:12 +0100)
committerDavid Mitchell <davem@iabyn.com>
Wed, 10 Aug 2016 15:34:04 +0000 (16:34 +0100)
commit84721d614eb7d9835d9a09505b0001c7be40a865
tree1c189557382a81051167278bce63d96843f5e14b
parent9c575c5c562583fedc6e491a509a388ce3b386bd
Partially pessimise in-place sorting

There's currently an optimisation that converts at compile-time

    @a = sort { .... } @a

into (approximately)

    sort { ... } \@a

Then at run time, rather than passing an svp pointer to the appropriate
sort routine which points to a list of SV*'s on the stack, pp_sort()
passes a pointer to @a's AvARRAY. This allows the array to be sorted
in-place, which is more efficient.

However, it has some issues. First, the @a visible to the sort routine
will be varying, whereas logically it should still hold the original list
of values until after the '@a = ...' assignment.

Secondly, the mergesort algorithm cureently used internally, when in
mid-sort, temporarily stores pointers in the array which aren't pointers
to SVs - this means that if @a elements are accessed mid-sort, it can
crash.

The solution to both these problems is for pp_sort() to push the elements
of @a onto the stack at the beginning, sort the stack (like normal sorts
do), then copy back to @a at the end. This is less efficient than before,
but is still a lot more efficient than executing separate padav and
aassign ops.

Here are benchmark results in raw instruction counts etc (lower is better)
for the sort line in this code block:

    my (@a, @b);
    @a = reverse 1..10;
    @b = sort { $a <=> $b } @a;

A is for a non-in-place sort, i.e. @b = sort ... @a;
B and C are for an inline sort, i.e. as above, but  @a = sort ... @a;
where B is blead before this commit and C is this commit.

                 A       B       C
            ------  ------  ------
        Ir  5238.0  2324.0  2772.0
        Dr  1464.0   649.0   765.0
        Dw   919.0   298.0   370.0
      COND   782.0   320.0   405.0
       IND    25.0    25.0    26.0

    COND_m    14.9    13.0    17.0
     IND_m     8.0     5.0     5.0

As can be seen, this partial pessimisation slows down in-place sorting by
round 20%, but overall in-place is still nearly twice the speed as without
the optimisation.

These are the figures for a plain numeric sort (which is optimised to use
a C comparison function); for other types of sort, the cost of the
comparator dominates, and so the slowdown is much less marked.
pp_sort.c
t/op/sort.t
t/perf/benchmarks