Revisiting Issue 1718

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Revisiting Issue 1718

Stephan Eggermont-3
I would be interested in improvements. This is just a straight translation
of the java code.

Yaroslavskiy's Dual-Pivot Quicksort seems to perform better for large collections:

A straightforward
defaultSort: i to: j
        self dualPivotQuicksort: i to: j split: 3

dualPivotQuicksort: left to: right split: div
        |len third m1 m2 pivot1 pivot2 less great dist newDiv|
        len := right - left.
        newDiv := div.
        len < 27 ifTrue: [
                left+1 to: right do: [ :i | | j |
                        j := i.
                        [(j>left) and: [(array at:j) < (array at: (j-1))]] whileTrue: [
                                array swap: j with: j-1.
                                j := j-1]]]
        ifFalse: [
                third := len // div.
                m1 := left+third.
                m2 := right-third.
                m1 <= left ifTrue: [m1 := left+1].
                m2 >= right ifTrue: [m2 := right-1].
                (array at: m1) < (array at: m2) ifTrue: [
                        array swap: m1 with: left.
                        array swap: m2 with: right]
                ifFalse: [
                        array swap: m1 with: right.
                        array swap: m2 with: left].
                pivot1 := array at: left.
                pivot2 := array at: right.
                less := left+1.
                great := right-1.
                less to: great do: [ :k |
                        (array at: k) < pivot1 ifTrue: [
                                array swap: k with: less.
                                less := less+1.]
                        ifFalse: [
                                (array at: k ) > pivot2 ifTrue: [
                                        [(k < great and: [(array at: great) > pivot2])] whileTrue: [
                                                great := great -1].
                                        array swap: k with: great.
                                        great := great-1.
                                        (array at: k) < pivot1 ifTrue: [
                                                array swap: k with: less.
                                                less := less+1]
                                ]
                        ]].
                dist := great-less.
                dist < 13 ifTrue: [ newDiv := div+1].
                array swap: (less-1) with: left.
                array swap: (great+1) with: right.
                self dualPivotQuicksort: left to: (less-2) split: newDiv.
                self dualPivotQuicksort: (great+2) to: right split: newDiv.
                (dist > (len -13) and: [ pivot1 ~= pivot2]) ifTrue:[
                        less to: great do: [ :k |
                                (array at: k) = pivot1 ifTrue: [
                                        array swap: k with: less.
                                        less := less+1]
                                ifFalse: [
                                        (array at: k) = pivot2 ifTrue: [
                                                array swap: k with: great.
                                                great := great - 1.
                                                (array at: k) = pivot1 ifTrue: [
                                                        array swap: k with: less.
                                                        less := less+1]
                                        ]
                                ]
                        ]
                ].
                pivot1 < pivot2 ifTrue: [
                        self dualPivotQuicksort: less to: great split: newDiv]
        ]

Do we have a performance test set for sorting?
Reply | Threaded
Open this post in threaded view
|

Re: Revisiting Issue 1718

Levente Uzonyi-2
For 1718:
- don't use #asSortedCollection for sorting.
- SortedCollection uses quicksort, not merge sort as stated in a comment.
That's why #asSortedCollection is slow when there are only a few different
values. Quicksort performance degrades to O(n^2) in this case, but that's
totally valid. However merge sort has guaranteed O(n*log(n)) runtime.

On Fri, 19 Nov 2010, Stephan Eggermont wrote:

> I would be interested in improvements. This is just a straight translation
> of the java code.

It has some errors. If you define this method in Array, replace 'array'
with 'self' and evaluate this code, it'll raise an error:

a := (1 to: 50) asArray.
[ a dualPivotQuicksort: 1 to: a size split: 3 ] bench

It's also not general. #sort: #sorted: and #asSortedCollection: use
a single block for comparison, while this method uses #<, #>, #= and #~=
in a mixed way.

>
> Yaroslavskiy's Dual-Pivot Quicksort seems to perform better for large collections:
>
> A straightforward
> defaultSort: i to: j
> self dualPivotQuicksort: i to: j split: 3
>
> dualPivotQuicksort: left to: right split: div
> |len third m1 m2 pivot1 pivot2 less great dist newDiv|
> len := right - left.
> newDiv := div.
> len < 27 ifTrue: [
> left+1 to: right do: [ :i | | j |
> j := i.
> [(j>left) and: [(array at:j) < (array at: (j-1))]] whileTrue: [
> array swap: j with: j-1.
> j := j-1]]]

That looks like an optimized bubble sort, ouch.


Levente

> ifFalse: [
> third := len // div.
> m1 := left+third.
> m2 := right-third.
> m1 <= left ifTrue: [m1 := left+1].
> m2 >= right ifTrue: [m2 := right-1].
> (array at: m1) < (array at: m2) ifTrue: [
> array swap: m1 with: left.
> array swap: m2 with: right]
> ifFalse: [
> array swap: m1 with: right.
> array swap: m2 with: left].
> pivot1 := array at: left.
> pivot2 := array at: right.
> less := left+1.
> great := right-1.
> less to: great do: [ :k |
> (array at: k) < pivot1 ifTrue: [
> array swap: k with: less.
> less := less+1.]
> ifFalse: [
> (array at: k ) > pivot2 ifTrue: [
> [(k < great and: [(array at: great) > pivot2])] whileTrue: [
> great := great -1].
> array swap: k with: great.
> great := great-1.
> (array at: k) < pivot1 ifTrue: [
> array swap: k with: less.
> less := less+1]
> ]
> ]].
> dist := great-less.
> dist < 13 ifTrue: [ newDiv := div+1].
> array swap: (less-1) with: left.
> array swap: (great+1) with: right.
> self dualPivotQuicksort: left to: (less-2) split: newDiv.
> self dualPivotQuicksort: (great+2) to: right split: newDiv.
> (dist > (len -13) and: [ pivot1 ~= pivot2]) ifTrue:[
> less to: great do: [ :k |
> (array at: k) = pivot1 ifTrue: [
> array swap: k with: less.
> less := less+1]
> ifFalse: [
> (array at: k) = pivot2 ifTrue: [
> array swap: k with: great.
> great := great - 1.
> (array at: k) = pivot1 ifTrue: [
> array swap: k with: less.
> less := less+1]
> ]
> ]
> ]
> ].
> pivot1 < pivot2 ifTrue: [
> self dualPivotQuicksort: less to: great split: newDiv]
> ]
>
> Do we have a performance test set for sorting?
>