From 78cee8b3bbf4173af79a4d02a26bbe8bea1cd175 Mon Sep 17 00:00:00 2001
From: David Symonds <dsymonds@golang.org>
Date: Thu, 14 Feb 2013 15:04:22 +1100
Subject: [PATCH] sort: use fewer comparisons when choosing pivot.

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      | 41 ++++++++++++++++++++++-----------------
 src/pkg/sort/sort_test.go | 24 +++++++++++++----------
 2 files changed, 37 insertions(+), 28 deletions(-)

diff --git a/src/pkg/sort/sort.go b/src/pkg/sort/sort.go
index 3f7a99730c8..e1096199240 100644
--- a/src/pkg/sort/sort.go
+++ b/src/pkg/sort/sort.go
@@ -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)
diff --git a/src/pkg/sort/sort_test.go b/src/pkg/sort/sort_test.go
index 439a3d5399e..5daf8482b9c 100644
--- a/src/pkg/sort/sort_test.go
+++ b/src/pkg/sort/sort_test.go
@@ -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
-- 
GitLab