mirror of
https://github.com/golang/go
synced 2024-11-05 23:36:12 -07:00
sort: forward fixed-type slice sorting to slices package
Forwards the following functions to the slices package: sort.Ints sort.Strings sort.Float64s sort.IntsAreSorted sort.StringsAreSorted sort.Float64sAreSorted benchstat results on the sort package's benchmarks: goos: linux goarch: amd64 pkg: sort cpu: 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz SearchWrappers-8 58.10n ± 0% 58.43n ± 1% +0.57% (p=0.004 n=10) SortString1K-8 76.53µ ± 1% 66.04µ ± 2% -13.71% (p=0.000 n=10) SortString1K_Slice-8 71.99µ ± 1% 72.32µ ± 2% ~ (p=0.481 n=10) StableString1K-8 92.66µ ± 1% 92.10µ ± 2% -0.61% (p=0.019 n=10) SortInt1K-8 34.31µ ± 0% 11.49µ ± 2% -66.50% (p=0.000 n=10) SortInt1K_Sorted-8 2699.5n ± 1% 959.0n ± 3% -64.47% (p=0.000 n=10) SortInt1K_Reversed-8 3.990µ ± 1% 1.429µ ± 4% -64.19% (p=0.000 n=10) SortInt1K_Mod8-8 13.695µ ± 1% 5.129µ ± 2% -62.55% (p=0.000 n=10) StableInt1K-8 46.22µ ± 1% 46.80µ ± 1% ~ (p=0.109 n=10) StableInt1K_Slice-8 44.12µ ± 1% 44.32µ ± 2% ~ (p=0.315 n=10) SortInt64K-8 3.848m ± 0% 1.857m ± 2% -51.76% (p=0.000 n=10) SortInt64K_Slice-8 3.690m ± 0% 3.740m ± 0% +1.36% (p=0.002 n=10) StableInt64K-8 3.901m ± 0% 3.917m ± 0% +0.42% (p=0.003 n=10) Sort1e2-8 32.22µ ± 2% 32.40µ ± 2% ~ (p=0.529 n=10) Stable1e2-8 54.11µ ± 1% 54.11µ ± 1% ~ (p=0.796 n=10) Sort1e4-8 5.998m ± 1% 5.993m ± 1% ~ (p=0.579 n=10) Stable1e4-8 15.23m ± 0% 15.32m ± 0% +0.59% (p=0.000 n=10) Sort1e6-8 902.8m ± 0% 904.3m ± 0% ~ (p=0.075 n=10) Stable1e6-8 3.089 ± 0% 3.089 ± 0% ~ (p=0.971 n=10) geomean 259.8µ 200.0µ -22.99% Most of the benchmarks are unaffected. The ones with significant reductions are precisely for the functions that were forwarded. This CL has to move some things around to avoid a circular dependency between sort and slices. Since sort depends on slices now, nothing in slices can depend on sort - not even in tests. Fixes #61180 Change-Id: Ic0e5f519863d96a139fada08aefb1bcdf4c7a9a3 Reviewed-on: https://go-review.googlesource.com/c/go/+/508135 Run-TryBot: Ian Lance Taylor <iant@google.com> Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Eli Bendersky <eliben@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
parent
12f3d6858e
commit
c3da3bcd8e
@ -77,8 +77,15 @@ var depsRules = `
|
||||
< internal/oserror, math/bits
|
||||
< RUNTIME;
|
||||
|
||||
RUNTIME
|
||||
< sort
|
||||
# slices depends on unsafe for overlapping check, cmp for comparison
|
||||
# semantics, and math/bits for # calculating bitlength of numbers.
|
||||
unsafe, cmp, math/bits
|
||||
< slices;
|
||||
|
||||
RUNTIME, slices
|
||||
< sort;
|
||||
|
||||
sort
|
||||
< container/heap;
|
||||
|
||||
RUNTIME
|
||||
@ -223,11 +230,6 @@ var depsRules = `
|
||||
< hash
|
||||
< hash/adler32, hash/crc32, hash/crc64, hash/fnv;
|
||||
|
||||
# slices depends on unsafe for overlapping check, cmp for comparison
|
||||
# semantics, and math/bits for # calculating bitlength of numbers.
|
||||
unsafe, cmp, math/bits
|
||||
< slices;
|
||||
|
||||
# math/big
|
||||
FMT, encoding/binary, math/rand
|
||||
< math/big;
|
||||
|
@ -2,13 +2,14 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package slices
|
||||
package slices_test
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"internal/race"
|
||||
"internal/testenv"
|
||||
"math"
|
||||
. "slices"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@ -999,25 +1000,6 @@ func BenchmarkReplace(b *testing.B) {
|
||||
|
||||
}
|
||||
|
||||
func TestRotate(t *testing.T) {
|
||||
const N = 10
|
||||
s := make([]int, 0, N)
|
||||
for n := 0; n < N; n++ {
|
||||
for r := 0; r < n; r++ {
|
||||
s = s[:0]
|
||||
for i := 0; i < n; i++ {
|
||||
s = append(s, i)
|
||||
}
|
||||
rotateLeft(s, r)
|
||||
for i := 0; i < n; i++ {
|
||||
if s[i] != (i+r)%n {
|
||||
t.Errorf("expected n=%d r=%d i:%d want:%d got:%d", n, r, i, (i+r)%n, s[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInsertGrowthRate(t *testing.T) {
|
||||
b := make([]byte, 1)
|
||||
maxCap := cap(b)
|
||||
|
@ -2,252 +2,14 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package slices
|
||||
package slices_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"slices"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// These benchmarks compare sorting a large slice of int with sort.Ints vs.
|
||||
// slices.Sort
|
||||
func makeRandomInts(n int) []int {
|
||||
rand.Seed(42)
|
||||
ints := make([]int, n)
|
||||
for i := 0; i < n; i++ {
|
||||
ints[i] = rand.Intn(n)
|
||||
}
|
||||
return ints
|
||||
}
|
||||
|
||||
func makeSortedInts(n int) []int {
|
||||
ints := make([]int, n)
|
||||
for i := 0; i < n; i++ {
|
||||
ints[i] = i
|
||||
}
|
||||
return ints
|
||||
}
|
||||
|
||||
func makeReversedInts(n int) []int {
|
||||
ints := make([]int, n)
|
||||
for i := 0; i < n; i++ {
|
||||
ints[i] = n - i
|
||||
}
|
||||
return ints
|
||||
}
|
||||
|
||||
const N = 100_000
|
||||
|
||||
func BenchmarkSortInts(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
ints := makeRandomInts(N)
|
||||
b.StartTimer()
|
||||
sort.Ints(ints)
|
||||
}
|
||||
}
|
||||
|
||||
func makeSortedStrings(n int) []string {
|
||||
x := make([]string, n)
|
||||
for i := 0; i < n; i++ {
|
||||
x[i] = strconv.Itoa(i)
|
||||
}
|
||||
Sort(x)
|
||||
return x
|
||||
}
|
||||
|
||||
func BenchmarkSlicesSortInts(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
ints := makeRandomInts(N)
|
||||
b.StartTimer()
|
||||
Sort(ints)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSlicesSortInts_Sorted(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
ints := makeSortedInts(N)
|
||||
b.StartTimer()
|
||||
Sort(ints)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSlicesSortInts_Reversed(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
ints := makeReversedInts(N)
|
||||
b.StartTimer()
|
||||
Sort(ints)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIntsAreSorted(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
ints := makeSortedInts(N)
|
||||
b.StartTimer()
|
||||
sort.IntsAreSorted(ints)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIsSorted(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
ints := makeSortedInts(N)
|
||||
b.StartTimer()
|
||||
IsSorted(ints)
|
||||
}
|
||||
}
|
||||
|
||||
// Since we're benchmarking these sorts against each other, make sure that they
|
||||
// generate similar results.
|
||||
func TestIntSorts(t *testing.T) {
|
||||
ints := makeRandomInts(200)
|
||||
ints2 := Clone(ints)
|
||||
|
||||
sort.Ints(ints)
|
||||
Sort(ints2)
|
||||
|
||||
for i := range ints {
|
||||
if ints[i] != ints2[i] {
|
||||
t.Fatalf("ints2 mismatch at %d; %d != %d", i, ints[i], ints2[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The following is a benchmark for sorting strings.
|
||||
|
||||
// makeRandomStrings generates n random strings with alphabetic runes of
|
||||
// varying lengths.
|
||||
func makeRandomStrings(n int) []string {
|
||||
rand.Seed(42)
|
||||
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
ss := make([]string, n)
|
||||
for i := 0; i < n; i++ {
|
||||
var sb strings.Builder
|
||||
slen := 2 + rand.Intn(50)
|
||||
for j := 0; j < slen; j++ {
|
||||
sb.WriteRune(letters[rand.Intn(len(letters))])
|
||||
}
|
||||
ss[i] = sb.String()
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
func TestStringSorts(t *testing.T) {
|
||||
ss := makeRandomStrings(200)
|
||||
ss2 := Clone(ss)
|
||||
|
||||
sort.Strings(ss)
|
||||
Sort(ss2)
|
||||
|
||||
for i := range ss {
|
||||
if ss[i] != ss2[i] {
|
||||
t.Fatalf("ss2 mismatch at %d; %s != %s", i, ss[i], ss2[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSortStrings(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
ss := makeRandomStrings(N)
|
||||
b.StartTimer()
|
||||
sort.Strings(ss)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSortStrings_Sorted(b *testing.B) {
|
||||
ss := makeSortedStrings(N)
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
sort.Strings(ss)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSlicesSortStrings(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
ss := makeRandomStrings(N)
|
||||
b.StartTimer()
|
||||
Sort(ss)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSlicesSortStrings_Sorted(b *testing.B) {
|
||||
ss := makeSortedStrings(N)
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
Sort(ss)
|
||||
}
|
||||
}
|
||||
|
||||
// These benchmarks compare sorting a slice of structs with sort.Sort vs.
|
||||
// slices.SortFunc.
|
||||
type myStruct struct {
|
||||
a, b, c, d string
|
||||
n int
|
||||
}
|
||||
|
||||
type myStructs []*myStruct
|
||||
|
||||
func (s myStructs) Len() int { return len(s) }
|
||||
func (s myStructs) Less(i, j int) bool { return s[i].n < s[j].n }
|
||||
func (s myStructs) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
func makeRandomStructs(n int) myStructs {
|
||||
rand.Seed(42)
|
||||
structs := make([]*myStruct, n)
|
||||
for i := 0; i < n; i++ {
|
||||
structs[i] = &myStruct{n: rand.Intn(n)}
|
||||
}
|
||||
return structs
|
||||
}
|
||||
|
||||
func TestStructSorts(t *testing.T) {
|
||||
ss := makeRandomStructs(200)
|
||||
ss2 := make([]*myStruct, len(ss))
|
||||
for i := range ss {
|
||||
ss2[i] = &myStruct{n: ss[i].n}
|
||||
}
|
||||
|
||||
sort.Sort(ss)
|
||||
SortFunc(ss2, func(a, b *myStruct) int { return a.n - b.n })
|
||||
|
||||
for i := range ss {
|
||||
if *ss[i] != *ss2[i] {
|
||||
t.Fatalf("ints2 mismatch at %d; %v != %v", i, *ss[i], *ss2[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSortStructs(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
ss := makeRandomStructs(N)
|
||||
b.StartTimer()
|
||||
sort.Sort(ss)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSortFuncStructs(b *testing.B) {
|
||||
cmpFunc := func(a, b *myStruct) int { return a.n - b.n }
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
ss := makeRandomStructs(N)
|
||||
b.StartTimer()
|
||||
SortFunc(ss, cmpFunc)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBinarySearchFloats(b *testing.B) {
|
||||
for _, size := range []int{16, 32, 64, 128, 512, 1024} {
|
||||
b.Run(fmt.Sprintf("Size%d", size), func(b *testing.B) {
|
||||
@ -259,12 +21,17 @@ func BenchmarkBinarySearchFloats(b *testing.B) {
|
||||
needle := (floats[midpoint] + floats[midpoint+1]) / 2
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
BinarySearch(floats, needle)
|
||||
slices.BinarySearch(floats, needle)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type myStruct struct {
|
||||
a, b, c, d string
|
||||
n int
|
||||
}
|
||||
|
||||
func BenchmarkBinarySearchFuncStruct(b *testing.B) {
|
||||
for _, size := range []int{16, 32, 64, 128, 512, 1024} {
|
||||
b.Run(fmt.Sprintf("Size%d", size), func(b *testing.B) {
|
||||
@ -277,7 +44,7 @@ func BenchmarkBinarySearchFuncStruct(b *testing.B) {
|
||||
lessFunc := func(a, b *myStruct) int { return a.n - b.n }
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
BinarySearchFunc(structs, needle, lessFunc)
|
||||
slices.BinarySearchFunc(structs, needle, lessFunc)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -2,14 +2,14 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package slices
|
||||
package slices_test
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"sort"
|
||||
. "slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
@ -17,7 +17,6 @@ import (
|
||||
|
||||
var ints = [...]int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
|
||||
var float64s = [...]float64{74.3, 59.0, math.Inf(1), 238.2, -784.0, 2.3, math.Inf(-1), 9845.768, -959.7485, 905, 7.8, 7.8, 74.3, 59.0, math.Inf(1), 238.2, -784.0, 2.3}
|
||||
var float64sWithNaNs = [...]float64{74.3, 59.0, math.Inf(1), 238.2, -784.0, 2.3, math.NaN(), math.NaN(), math.Inf(-1), 9845.768, -959.7485, 905, 7.8, 7.8}
|
||||
var strs = [...]string{"", "Hello", "foo", "bar", "foo", "f00", "%*&^*&^&", "***"}
|
||||
|
||||
func TestSortIntSlice(t *testing.T) {
|
||||
@ -47,23 +46,6 @@ func TestSortFloat64Slice(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSortFloat64SliceWithNaNs(t *testing.T) {
|
||||
data := float64sWithNaNs[:]
|
||||
data2 := Clone(data)
|
||||
|
||||
Sort(data)
|
||||
sort.Float64s(data2)
|
||||
|
||||
if !IsSorted(data) {
|
||||
t.Error("IsSorted indicates data isn't sorted")
|
||||
}
|
||||
|
||||
// Compare for equality using cmp.Compare, which considers NaNs equal.
|
||||
if !EqualFunc(data, data2, func(a, b float64) bool { return cmp.Compare(a, b) == 0 }) {
|
||||
t.Errorf("mismatch between Sort and sort.Float64: got %v, want %v", data, data2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSortStringSlice(t *testing.T) {
|
||||
data := Clone(strs[:])
|
||||
Sort(data)
|
||||
|
@ -18,6 +18,9 @@ import (
|
||||
//
|
||||
// The less function must satisfy the same requirements as
|
||||
// the Interface type's Less method.
|
||||
//
|
||||
// Note: in many situations, the newer slices.SortFunc function is more
|
||||
// ergonomic and runs faster.
|
||||
func Slice(x any, less func(i, j int) bool) {
|
||||
rv := reflectlite.ValueOf(x)
|
||||
swap := reflectlite.Swapper(x)
|
||||
@ -32,6 +35,9 @@ func Slice(x any, less func(i, j int) bool) {
|
||||
//
|
||||
// The less function must satisfy the same requirements as
|
||||
// the Interface type's Less method.
|
||||
//
|
||||
// Note: in many situations, the newer slices.SortStableFunc function is more
|
||||
// ergonomic and runs faster.
|
||||
func SliceStable(x any, less func(i, j int) bool) {
|
||||
rv := reflectlite.ValueOf(x)
|
||||
swap := reflectlite.Swapper(x)
|
||||
@ -40,6 +46,9 @@ func SliceStable(x any, less func(i, j int) bool) {
|
||||
|
||||
// SliceIsSorted reports whether the slice x is sorted according to the provided less function.
|
||||
// It panics if x is not a slice.
|
||||
//
|
||||
// Note: in many situations, the newer slices.IsSortedFunc function is more
|
||||
// ergonomic and runs faster.
|
||||
func SliceIsSorted(x any, less func(i, j int) bool) bool {
|
||||
rv := reflectlite.ValueOf(x)
|
||||
n := rv.Len()
|
||||
|
@ -161,35 +161,35 @@ func (x StringSlice) Sort() { Sort(x) }
|
||||
|
||||
// Ints sorts a slice of ints in increasing order.
|
||||
//
|
||||
// Note: consider using the newer slices.Sort function, which runs faster.
|
||||
func Ints(x []int) { Sort(IntSlice(x)) }
|
||||
// Note: as of Go 1.22, this function simply calls slices.Sort.
|
||||
func Ints(x []int) { intsImpl(x) }
|
||||
|
||||
// Float64s sorts a slice of float64s in increasing order.
|
||||
// Not-a-number (NaN) values are ordered before other values.
|
||||
//
|
||||
// Note: consider using the newer slices.Sort function, which runs faster.
|
||||
func Float64s(x []float64) { Sort(Float64Slice(x)) }
|
||||
// Note: as of Go 1.22, this function simply calls slices.Sort.
|
||||
func Float64s(x []float64) { float64sImpl(x) }
|
||||
|
||||
// Strings sorts a slice of strings in increasing order.
|
||||
//
|
||||
// Note: consider using the newer slices.Sort function, which runs faster.
|
||||
func Strings(x []string) { Sort(StringSlice(x)) }
|
||||
// Note: as of Go 1.22, this function simply calls slices.Sort.
|
||||
func Strings(x []string) { stringsImpl(x) }
|
||||
|
||||
// IntsAreSorted reports whether the slice x is sorted in increasing order.
|
||||
//
|
||||
// Note: consider using the newer slices.IsSorted function, which runs faster.
|
||||
func IntsAreSorted(x []int) bool { return IsSorted(IntSlice(x)) }
|
||||
// Note: as of Go 1.22, this function simply calls slices.IsSorted.
|
||||
func IntsAreSorted(x []int) bool { return intsAreSortedImpl(x) }
|
||||
|
||||
// Float64sAreSorted reports whether the slice x is sorted in increasing order,
|
||||
// with not-a-number (NaN) values before any other values.
|
||||
//
|
||||
// Note: consider using the newer slices.IsSorted function, which runs faster.
|
||||
func Float64sAreSorted(x []float64) bool { return IsSorted(Float64Slice(x)) }
|
||||
// Note: as of Go 1.22, this function simply calls slices.IsSorted.
|
||||
func Float64sAreSorted(x []float64) bool { return float64sAreSortedImpl(x) }
|
||||
|
||||
// StringsAreSorted reports whether the slice x is sorted in increasing order.
|
||||
//
|
||||
// Note: consider using the newer slices.IsSorted function, which runs faster.
|
||||
func StringsAreSorted(x []string) bool { return IsSorted(StringSlice(x)) }
|
||||
// Note: as of Go 1.22, this function simply calls slices.IsSorted.
|
||||
func StringsAreSorted(x []string) bool { return stringsAreSortedImpl(x) }
|
||||
|
||||
// Notes on stable sorting:
|
||||
// The used algorithms are simple and provable correct on all input and use
|
||||
|
15
src/sort/sort_impl_120.go
Normal file
15
src/sort/sort_impl_120.go
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.21
|
||||
|
||||
package sort
|
||||
|
||||
func intsImpl(x []int) { Sort(IntSlice(x)) }
|
||||
func float64sImpl(x []float64) { Sort(Float64Slice(x)) }
|
||||
func stringsImpl(x []string) { Sort(StringSlice(x)) }
|
||||
|
||||
func intsAreSortedImpl(x []int) bool { return IsSorted(IntSlice(x)) }
|
||||
func float64sAreSortedImpl(x []float64) bool { return IsSorted(Float64Slice(x)) }
|
||||
func stringsAreSortedImpl(x []string) bool { return IsSorted(StringSlice(x)) }
|
22
src/sort/sort_impl_go121.go
Normal file
22
src/sort/sort_impl_go121.go
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.21
|
||||
|
||||
// Starting with Go 1.21, we can leverage the new generic functions from the
|
||||
// slices package to implement some `sort` functions faster. However, until
|
||||
// the bootstrap compiler uses Go 1.21 or later, we keep a fallback version
|
||||
// in sort_impl_120.go that retains the old implementation.
|
||||
|
||||
package sort
|
||||
|
||||
import "slices"
|
||||
|
||||
func intsImpl(x []int) { slices.Sort(x) }
|
||||
func float64sImpl(x []float64) { slices.Sort(x) }
|
||||
func stringsImpl(x []string) { slices.Sort(x) }
|
||||
|
||||
func intsAreSortedImpl(x []int) bool { return slices.IsSorted(x) }
|
||||
func float64sAreSortedImpl(x []float64) bool { return slices.IsSorted(x) }
|
||||
func stringsAreSortedImpl(x []string) bool { return slices.IsSorted(x) }
|
201
src/sort/sort_slices_benchmark_test.go
Normal file
201
src/sort/sort_slices_benchmark_test.go
Normal file
@ -0,0 +1,201 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sort_test
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"slices"
|
||||
. "sort"
|
||||
"strconv"
|
||||
stringspkg "strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Benchmarks comparing sorting from the slices package with functions from
|
||||
// the sort package (avoiding functions that are just forwarding to the slices
|
||||
// package).
|
||||
|
||||
func makeRandomInts(n int) []int {
|
||||
rand.Seed(42)
|
||||
ints := make([]int, n)
|
||||
for i := 0; i < n; i++ {
|
||||
ints[i] = rand.Intn(n)
|
||||
}
|
||||
return ints
|
||||
}
|
||||
|
||||
func makeSortedInts(n int) []int {
|
||||
ints := make([]int, n)
|
||||
for i := 0; i < n; i++ {
|
||||
ints[i] = i
|
||||
}
|
||||
return ints
|
||||
}
|
||||
|
||||
func makeReversedInts(n int) []int {
|
||||
ints := make([]int, n)
|
||||
for i := 0; i < n; i++ {
|
||||
ints[i] = n - i
|
||||
}
|
||||
return ints
|
||||
}
|
||||
|
||||
func makeSortedStrings(n int) []string {
|
||||
x := make([]string, n)
|
||||
for i := 0; i < n; i++ {
|
||||
x[i] = strconv.Itoa(i)
|
||||
}
|
||||
Strings(x)
|
||||
return x
|
||||
}
|
||||
|
||||
const N = 100_000
|
||||
|
||||
func BenchmarkSortInts(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
ints := makeRandomInts(N)
|
||||
b.StartTimer()
|
||||
Sort(IntSlice(ints))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSlicesSortInts(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
ints := makeRandomInts(N)
|
||||
b.StartTimer()
|
||||
slices.Sort(ints)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSortIsSorted(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
ints := makeSortedInts(N)
|
||||
b.StartTimer()
|
||||
IsSorted(IntSlice(ints))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSlicesIsSorted(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
ints := makeSortedInts(N)
|
||||
b.StartTimer()
|
||||
slices.IsSorted(ints)
|
||||
}
|
||||
}
|
||||
|
||||
// makeRandomStrings generates n random strings with alphabetic runes of
|
||||
// varying lengths.
|
||||
func makeRandomStrings(n int) []string {
|
||||
rand.Seed(42)
|
||||
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
ss := make([]string, n)
|
||||
for i := 0; i < n; i++ {
|
||||
var sb stringspkg.Builder
|
||||
slen := 2 + rand.Intn(50)
|
||||
for j := 0; j < slen; j++ {
|
||||
sb.WriteRune(letters[rand.Intn(len(letters))])
|
||||
}
|
||||
ss[i] = sb.String()
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
func BenchmarkSortStrings(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
ss := makeRandomStrings(N)
|
||||
b.StartTimer()
|
||||
Sort(StringSlice(ss))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSlicesSortStrings(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
ss := makeRandomStrings(N)
|
||||
b.StartTimer()
|
||||
slices.Sort(ss)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSortStrings_Sorted(b *testing.B) {
|
||||
ss := makeSortedStrings(N)
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
Sort(StringSlice(ss))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSlicesSortStrings_Sorted(b *testing.B) {
|
||||
ss := makeSortedStrings(N)
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
slices.Sort(ss)
|
||||
}
|
||||
}
|
||||
|
||||
// These benchmarks compare sorting a slice of structs with sort.Sort vs.
|
||||
// slices.SortFunc.
|
||||
type myStruct struct {
|
||||
a, b, c, d string
|
||||
n int
|
||||
}
|
||||
|
||||
type myStructs []*myStruct
|
||||
|
||||
func (s myStructs) Len() int { return len(s) }
|
||||
func (s myStructs) Less(i, j int) bool { return s[i].n < s[j].n }
|
||||
func (s myStructs) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
func makeRandomStructs(n int) myStructs {
|
||||
rand.Seed(42)
|
||||
structs := make([]*myStruct, n)
|
||||
for i := 0; i < n; i++ {
|
||||
structs[i] = &myStruct{n: rand.Intn(n)}
|
||||
}
|
||||
return structs
|
||||
}
|
||||
|
||||
func TestStructSorts(t *testing.T) {
|
||||
ss := makeRandomStructs(200)
|
||||
ss2 := make([]*myStruct, len(ss))
|
||||
for i := range ss {
|
||||
ss2[i] = &myStruct{n: ss[i].n}
|
||||
}
|
||||
|
||||
Sort(ss)
|
||||
slices.SortFunc(ss2, func(a, b *myStruct) int { return a.n - b.n })
|
||||
|
||||
for i := range ss {
|
||||
if *ss[i] != *ss2[i] {
|
||||
t.Fatalf("ints2 mismatch at %d; %v != %v", i, *ss[i], *ss2[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSortStructs(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
ss := makeRandomStructs(N)
|
||||
b.StartTimer()
|
||||
Sort(ss)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSortFuncStructs(b *testing.B) {
|
||||
cmpFunc := func(a, b *myStruct) int { return a.n - b.n }
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
ss := makeRandomStructs(N)
|
||||
b.StartTimer()
|
||||
slices.SortFunc(ss, cmpFunc)
|
||||
}
|
||||
}
|
@ -5,10 +5,12 @@
|
||||
package sort_test
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"fmt"
|
||||
"internal/testenv"
|
||||
"math"
|
||||
"math/rand"
|
||||
"slices"
|
||||
. "sort"
|
||||
"strconv"
|
||||
stringspkg "strings"
|
||||
@ -39,6 +41,20 @@ func TestSortFloat64Slice(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Compare Sort with slices.Sort sorting a float64 slice containing NaNs.
|
||||
func TestSortFloat64sCompareSlicesSort(t *testing.T) {
|
||||
slice1 := slices.Clone(float64s[:])
|
||||
slice2 := slices.Clone(float64s[:])
|
||||
|
||||
Sort(Float64Slice(slice1))
|
||||
slices.Sort(slice2)
|
||||
|
||||
// Compare for equality using cmp.Compare, which considers NaNs equal.
|
||||
if !slices.EqualFunc(slice1, slice1, func(a, b float64) bool { return cmp.Compare(a, b) == 0 }) {
|
||||
t.Errorf("mismatch between Sort and slices.Sort: got %v, want %v", slice1, slice2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSortStringSlice(t *testing.T) {
|
||||
data := strings
|
||||
a := StringSlice(data[0:])
|
||||
|
Loading…
Reference in New Issue
Block a user