mirror of https://github.com/golang/go.git
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
This commit is contained in:
parent
7f284f85f9
commit
78cee8b3bb
|
|
@ -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
|
||||
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, b) { // data[b] = pivot
|
||||
data.Swap(a, b)
|
||||
a++
|
||||
b++
|
||||
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(pivot, c-1) { // data[c-1] > pivot
|
||||
c--
|
||||
continue
|
||||
}
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue