]> Cypherpunks repositories - gostls13.git/commitdiff
sort: forward fixed-type slice sorting to slices package
authorEli Bendersky <eliben@golang.org>
Thu, 6 Jul 2023 15:19:25 +0000 (08:19 -0700)
committerEli Bendersky <eliben@google.com>
Fri, 21 Jul 2023 13:00:18 +0000 (13:00 +0000)
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>

src/go/build/deps_test.go
src/slices/slices_test.go
src/slices/sort_benchmark_test.go
src/slices/sort_test.go
src/sort/slice.go
src/sort/sort.go
src/sort/sort_impl_120.go [new file with mode: 0644]
src/sort/sort_impl_go121.go [new file with mode: 0644]
src/sort/sort_slices_benchmark_test.go [new file with mode: 0644]
src/sort/sort_test.go

index 2f335068b8a62fea40cc699681d333c983ab243b..bdb09737b039542077a57734135b9051c09f224c 100644 (file)
@@ -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;
index e6da3b0e039c5d1a057ef85a99e2f362d7a2457a..8ea93c66d766861bcde3a2a31ce3a6d51fa11cc4 100644 (file)
@@ -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)
index 0f088425949df31a4b70c3992f29469c546f4086..d73a3182c91aec4d2e4637b308eedba62f83550b 100644 (file)
 // 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)
                        }
                })
        }
index af0585935dfd79eee306d1a837a9ca6493df3c76..7aaf954214f40c4690c2b1da5f2040dfeae98f2d 100644 (file)
@@ -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)
index d0b2102013876cdd935d11867f7dd7a8fbbd4467..73ba548a474ac598f4f7dd5850969dd70da866b6 100644 (file)
@@ -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()
index 1760e12c25621b596f22d51376fc29aa5ff35e97..8ea62a5e6a5b8a31df044aab77b8652ca686dd75 100644 (file)
@@ -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
diff --git a/src/sort/sort_impl_120.go b/src/sort/sort_impl_120.go
new file mode 100644 (file)
index 0000000..5980da6
--- /dev/null
@@ -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)) }
diff --git a/src/sort/sort_impl_go121.go b/src/sort/sort_impl_go121.go
new file mode 100644 (file)
index 0000000..0a6a6a6
--- /dev/null
@@ -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) }
diff --git a/src/sort/sort_slices_benchmark_test.go b/src/sort/sort_slices_benchmark_test.go
new file mode 100644 (file)
index 0000000..37f3b1b
--- /dev/null
@@ -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)
+       }
+}
index 862bba2d4415a2fe2ec80e34ca79844292a33799..62f51ba6398ff68ed2d4877a0aa4a0018f81ddea 100644 (file)
@@ -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:])