Commit 78cee8b3 authored by David Symonds's avatar David Symonds

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
parent 7f284f85
...@@ -124,26 +124,31 @@ func doPivot(data Interface, lo, hi int) (midlo, midhi int) { ...@@ -124,26 +124,31 @@ func doPivot(data Interface, lo, hi int) (midlo, midhi int) {
// into the middle of the slice. // into the middle of the slice.
pivot := lo pivot := lo
a, b, c, d := lo+1, lo+1, hi, hi a, b, c, d := lo+1, lo+1, hi, hi
for b < c { for {
if data.Less(b, pivot) { // data[b] < pivot for b < c {
b++ if data.Less(b, pivot) { // data[b] < pivot
continue b++
} } else if !data.Less(pivot, b) { // data[b] = pivot
if !data.Less(pivot, b) { // data[b] = pivot data.Swap(a, b)
data.Swap(a, b) a++
a++ b++
b++ } else {
continue break
}
} }
if data.Less(pivot, c-1) { // data[c-1] > pivot for b < c {
c-- if data.Less(pivot, c-1) { // data[c-1] > pivot
continue 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 if b >= c {
data.Swap(c-1, d-1) break
c--
d--
continue
} }
// data[b] > pivot; data[c-1] < pivot // data[b] > pivot; data[c-1] < pivot
data.Swap(b, c-1) data.Swap(b, c-1)
......
...@@ -168,15 +168,18 @@ const ( ...@@ -168,15 +168,18 @@ const (
) )
type testingData struct { type testingData struct {
desc string desc string
t *testing.T t *testing.T
data []int data []int
maxswap int // number of swaps allowed maxswap int // number of swaps allowed
nswap int ncmp, nswap int
} }
func (d *testingData) Len() int { return len(d.data) } 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) Less(i, j int) bool {
d.ncmp++
return d.data[i] < d.data[j]
}
func (d *testingData) Swap(i, j int) { func (d *testingData) Swap(i, j int) {
if d.nswap >= d.maxswap { if d.nswap >= d.maxswap {
d.t.Errorf("%s: used %d swaps sorting slice of %d", d.desc, d.nswap, len(d.data)) 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)) { ...@@ -209,8 +212,7 @@ func testBentleyMcIlroy(t *testing.T, sort func(Interface)) {
dists := []string{"sawtooth", "rand", "stagger", "plateau", "shuffle"} dists := []string{"sawtooth", "rand", "stagger", "plateau", "shuffle"}
modes := []string{"copy", "reverse", "reverse1", "reverse2", "sort", "dither"} modes := []string{"copy", "reverse", "reverse1", "reverse2", "sort", "dither"}
var tmp1, tmp2 [1025]int var tmp1, tmp2 [1025]int
for ni := 0; ni < len(sizes); ni++ { for _, n := range sizes {
n := sizes[ni]
for m := 1; m < 2*n; m *= 2 { for m := 1; m < 2*n; m *= 2 {
for dist := 0; dist < _NDist; dist++ { for dist := 0; dist < _NDist; dist++ {
j := 0 j := 0
...@@ -276,8 +278,10 @@ func testBentleyMcIlroy(t *testing.T, sort func(Interface)) { ...@@ -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]) 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) 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 // If we were testing C qsort, we'd have to make a copy
// of the slice and sort it ourselves and then compare // of the slice and sort it ourselves and then compare
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment