]> Cypherpunks repositories - gostls13.git/commitdiff
sort: use fewer comparisons when choosing pivot.
authorDavid Symonds <dsymonds@golang.org>
Thu, 14 Feb 2013 04:04:22 +0000 (15:04 +1100)
committerDavid Symonds <dsymonds@golang.org>
Thu, 14 Feb 2013 04:04:22 +0000 (15:04 +1100)
This is based on rsc's code posted to issue 2585.

Benchmark results are greatly improved:
        benchmark                old ns/op    new ns/op    delta
        BenchmarkSortString1K       564397       445897  -21.00%
        BenchmarkSortInt1K          270889       221249  -18.32%
        BenchmarkSortInt64K       26850765     21351967  -20.48%

Eyeballing a sampling of the raw number of comparisons shows a drop
on the order of 20-30% almost everywhere. The test input data that
doesn't match that are some of sawtooth/rand/plateau distributions,
where there is no change in the number of comparisons; that is,
there are no situations where this makes *more* comparisons.

Fixes #2585.

R=iant, rsc
CC=golang-dev
https://golang.org/cl/7306098

src/pkg/sort/sort.go
src/pkg/sort/sort_test.go

index 3f7a99730c8cef665a0df6e046822ffc61d46eab..e109619924013850b379b720f73ce68cec1fbf72 100644 (file)
@@ -124,26 +124,31 @@ func doPivot(data Interface, lo, hi int) (midlo, midhi int) {
        // into the middle of the slice.
        pivot := lo
        a, b, c, d := lo+1, lo+1, hi, hi
-       for b < c {
-               if data.Less(b, pivot) { // data[b] < pivot
-                       b++
-                       continue
-               }
-               if !data.Less(pivot, b) { // data[b] = pivot
-                       data.Swap(a, b)
-                       a++
-                       b++
-                       continue
+       for {
+               for b < c {
+                       if data.Less(b, pivot) { // data[b] < pivot
+                               b++
+                       } else if !data.Less(pivot, b) { // data[b] = pivot
+                               data.Swap(a, b)
+                               a++
+                               b++
+                       } else {
+                               break
+                       }
                }
-               if data.Less(pivot, c-1) { // data[c-1] > pivot
-                       c--
-                       continue
+               for b < c {
+                       if data.Less(pivot, c-1) { // data[c-1] > pivot
+                               c--
+                       } else if !data.Less(c-1, pivot) { // data[c-1] = pivot
+                               data.Swap(c-1, d-1)
+                               c--
+                               d--
+                       } else {
+                               break
+                       }
                }
-               if !data.Less(c-1, pivot) { // data[c-1] = pivot
-                       data.Swap(c-1, d-1)
-                       c--
-                       d--
-                       continue
+               if b >= c {
+                       break
                }
                // data[b] > pivot; data[c-1] < pivot
                data.Swap(b, c-1)
index 439a3d5399ed7d7f31c5378a3ede8e96556406a8..5daf8482b9c271581f62948f7a1d2ff6b41f4bb3 100644 (file)
@@ -168,15 +168,18 @@ const (
 )
 
 type testingData struct {
-       desc    string
-       t       *testing.T
-       data    []int
-       maxswap int // number of swaps allowed
-       nswap   int
+       desc        string
+       t           *testing.T
+       data        []int
+       maxswap     int // number of swaps allowed
+       ncmp, nswap int
 }
 
-func (d *testingData) Len() int           { return len(d.data) }
-func (d *testingData) Less(i, j int) bool { return d.data[i] < d.data[j] }
+func (d *testingData) Len() int { return len(d.data) }
+func (d *testingData) Less(i, j int) bool {
+       d.ncmp++
+       return d.data[i] < d.data[j]
+}
 func (d *testingData) Swap(i, j int) {
        if d.nswap >= d.maxswap {
                d.t.Errorf("%s: used %d swaps sorting slice of %d", d.desc, d.nswap, len(d.data))
@@ -209,8 +212,7 @@ func testBentleyMcIlroy(t *testing.T, sort func(Interface)) {
        dists := []string{"sawtooth", "rand", "stagger", "plateau", "shuffle"}
        modes := []string{"copy", "reverse", "reverse1", "reverse2", "sort", "dither"}
        var tmp1, tmp2 [1025]int
-       for ni := 0; ni < len(sizes); ni++ {
-               n := sizes[ni]
+       for _, n := range sizes {
                for m := 1; m < 2*n; m *= 2 {
                        for dist := 0; dist < _NDist; dist++ {
                                j := 0
@@ -276,8 +278,10 @@ func testBentleyMcIlroy(t *testing.T, sort func(Interface)) {
                                        }
 
                                        desc := fmt.Sprintf("n=%d m=%d dist=%s mode=%s", n, m, dists[dist], modes[mode])
-                                       d := &testingData{desc, t, mdata[0:n], n * lg(n) * 12 / 10, 0}
+                                       d := &testingData{desc: desc, t: t, data: mdata[0:n], maxswap: n * lg(n) * 12 / 10}
                                        sort(d)
+                                       // Uncomment if you are trying to improve the number of compares/swaps.
+                                       //t.Logf("%s: ncmp=%d, nswp=%d", desc, d.ncmp, d.nswap)
 
                                        // If we were testing C qsort, we'd have to make a copy
                                        // of the slice and sort it ourselves and then compare