From 78cee8b3bbf4173af79a4d02a26bbe8bea1cd175 Mon Sep 17 00:00:00 2001 From: David Symonds 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 3f7a99730c..e109619924 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 439a3d5399..5daf8482b9 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 -- 2.48.1