golang.org/x/exp@v0.0.0-20240506185415-9bf2ced13842/slices/sort_benchmark_test.go (about)

     1  // Copyright 2022 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package slices
     6  
     7  import (
     8  	"fmt"
     9  	"math/rand"
    10  	"sort"
    11  	"strconv"
    12  	"strings"
    13  	"testing"
    14  )
    15  
    16  // These benchmarks compare sorting a large slice of int with sort.Ints vs.
    17  // slices.Sort
    18  func makeRandomInts(n int) []int {
    19  	rand.Seed(42)
    20  	ints := make([]int, n)
    21  	for i := 0; i < n; i++ {
    22  		ints[i] = rand.Intn(n)
    23  	}
    24  	return ints
    25  }
    26  
    27  func makeSortedInts(n int) []int {
    28  	ints := make([]int, n)
    29  	for i := 0; i < n; i++ {
    30  		ints[i] = i
    31  	}
    32  	return ints
    33  }
    34  
    35  func makeReversedInts(n int) []int {
    36  	ints := make([]int, n)
    37  	for i := 0; i < n; i++ {
    38  		ints[i] = n - i
    39  	}
    40  	return ints
    41  }
    42  
    43  const N = 100_000
    44  
    45  func BenchmarkSortInts(b *testing.B) {
    46  	for i := 0; i < b.N; i++ {
    47  		b.StopTimer()
    48  		ints := makeRandomInts(N)
    49  		b.StartTimer()
    50  		sort.Ints(ints)
    51  	}
    52  }
    53  
    54  func makeSortedStrings(n int) []string {
    55  	x := make([]string, n)
    56  	for i := 0; i < n; i++ {
    57  		x[i] = strconv.Itoa(i)
    58  	}
    59  	Sort(x)
    60  	return x
    61  }
    62  
    63  func BenchmarkSlicesSortInts(b *testing.B) {
    64  	for i := 0; i < b.N; i++ {
    65  		b.StopTimer()
    66  		ints := makeRandomInts(N)
    67  		b.StartTimer()
    68  		Sort(ints)
    69  	}
    70  }
    71  
    72  func BenchmarkSlicesSortInts_Sorted(b *testing.B) {
    73  	for i := 0; i < b.N; i++ {
    74  		b.StopTimer()
    75  		ints := makeSortedInts(N)
    76  		b.StartTimer()
    77  		Sort(ints)
    78  	}
    79  }
    80  
    81  func BenchmarkSlicesSortInts_Reversed(b *testing.B) {
    82  	for i := 0; i < b.N; i++ {
    83  		b.StopTimer()
    84  		ints := makeReversedInts(N)
    85  		b.StartTimer()
    86  		Sort(ints)
    87  	}
    88  }
    89  
    90  func BenchmarkIntsAreSorted(b *testing.B) {
    91  	for i := 0; i < b.N; i++ {
    92  		b.StopTimer()
    93  		ints := makeSortedInts(N)
    94  		b.StartTimer()
    95  		sort.IntsAreSorted(ints)
    96  	}
    97  }
    98  
    99  func BenchmarkIsSorted(b *testing.B) {
   100  	for i := 0; i < b.N; i++ {
   101  		b.StopTimer()
   102  		ints := makeSortedInts(N)
   103  		b.StartTimer()
   104  		IsSorted(ints)
   105  	}
   106  }
   107  
   108  // Since we're benchmarking these sorts against each other, make sure that they
   109  // generate similar results.
   110  func TestIntSorts(t *testing.T) {
   111  	ints := makeRandomInts(200)
   112  	ints2 := Clone(ints)
   113  
   114  	sort.Ints(ints)
   115  	Sort(ints2)
   116  
   117  	for i := range ints {
   118  		if ints[i] != ints2[i] {
   119  			t.Fatalf("ints2 mismatch at %d; %d != %d", i, ints[i], ints2[i])
   120  		}
   121  	}
   122  }
   123  
   124  // The following is a benchmark for sorting strings.
   125  
   126  // makeRandomStrings generates n random strings with alphabetic runes of
   127  // varying lengths.
   128  func makeRandomStrings(n int) []string {
   129  	rand.Seed(42)
   130  	var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
   131  	ss := make([]string, n)
   132  	for i := 0; i < n; i++ {
   133  		var sb strings.Builder
   134  		slen := 2 + rand.Intn(50)
   135  		for j := 0; j < slen; j++ {
   136  			sb.WriteRune(letters[rand.Intn(len(letters))])
   137  		}
   138  		ss[i] = sb.String()
   139  	}
   140  	return ss
   141  }
   142  
   143  func TestStringSorts(t *testing.T) {
   144  	ss := makeRandomStrings(200)
   145  	ss2 := Clone(ss)
   146  
   147  	sort.Strings(ss)
   148  	Sort(ss2)
   149  
   150  	for i := range ss {
   151  		if ss[i] != ss2[i] {
   152  			t.Fatalf("ss2 mismatch at %d; %s != %s", i, ss[i], ss2[i])
   153  		}
   154  	}
   155  }
   156  
   157  func BenchmarkSortStrings(b *testing.B) {
   158  	for i := 0; i < b.N; i++ {
   159  		b.StopTimer()
   160  		ss := makeRandomStrings(N)
   161  		b.StartTimer()
   162  		sort.Strings(ss)
   163  	}
   164  }
   165  
   166  func BenchmarkSortStrings_Sorted(b *testing.B) {
   167  	ss := makeSortedStrings(N)
   168  	b.ResetTimer()
   169  
   170  	for i := 0; i < b.N; i++ {
   171  		sort.Strings(ss)
   172  	}
   173  }
   174  
   175  func BenchmarkSlicesSortStrings(b *testing.B) {
   176  	for i := 0; i < b.N; i++ {
   177  		b.StopTimer()
   178  		ss := makeRandomStrings(N)
   179  		b.StartTimer()
   180  		Sort(ss)
   181  	}
   182  }
   183  
   184  func BenchmarkSlicesSortStrings_Sorted(b *testing.B) {
   185  	ss := makeSortedStrings(N)
   186  	b.ResetTimer()
   187  
   188  	for i := 0; i < b.N; i++ {
   189  		Sort(ss)
   190  	}
   191  }
   192  
   193  // These benchmarks compare sorting a slice of structs with sort.Sort vs.
   194  // slices.SortFunc.
   195  type myStruct struct {
   196  	a, b, c, d string
   197  	n          int
   198  }
   199  
   200  type myStructs []*myStruct
   201  
   202  func (s myStructs) Len() int           { return len(s) }
   203  func (s myStructs) Less(i, j int) bool { return s[i].n < s[j].n }
   204  func (s myStructs) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
   205  
   206  func makeRandomStructs(n int) myStructs {
   207  	rand.Seed(42)
   208  	structs := make([]*myStruct, n)
   209  	for i := 0; i < n; i++ {
   210  		structs[i] = &myStruct{n: rand.Intn(n)}
   211  	}
   212  	return structs
   213  }
   214  
   215  func TestStructSorts(t *testing.T) {
   216  	ss := makeRandomStructs(200)
   217  	ss2 := make([]*myStruct, len(ss))
   218  	for i := range ss {
   219  		ss2[i] = &myStruct{n: ss[i].n}
   220  	}
   221  
   222  	sort.Sort(ss)
   223  	SortFunc(ss2, func(a, b *myStruct) int { return a.n - b.n })
   224  
   225  	for i := range ss {
   226  		if *ss[i] != *ss2[i] {
   227  			t.Fatalf("ints2 mismatch at %d; %v != %v", i, *ss[i], *ss2[i])
   228  		}
   229  	}
   230  }
   231  
   232  func BenchmarkSortStructs(b *testing.B) {
   233  	for i := 0; i < b.N; i++ {
   234  		b.StopTimer()
   235  		ss := makeRandomStructs(N)
   236  		b.StartTimer()
   237  		sort.Sort(ss)
   238  	}
   239  }
   240  
   241  func BenchmarkSortFuncStructs(b *testing.B) {
   242  	cmpFunc := func(a, b *myStruct) int { return a.n - b.n }
   243  	for i := 0; i < b.N; i++ {
   244  		b.StopTimer()
   245  		ss := makeRandomStructs(N)
   246  		b.StartTimer()
   247  		SortFunc(ss, cmpFunc)
   248  	}
   249  }
   250  
   251  func BenchmarkBinarySearchFloats(b *testing.B) {
   252  	for _, size := range []int{16, 32, 64, 128, 512, 1024} {
   253  		b.Run(fmt.Sprintf("Size%d", size), func(b *testing.B) {
   254  			floats := make([]float64, size)
   255  			for i := range floats {
   256  				floats[i] = float64(i)
   257  			}
   258  			midpoint := len(floats) / 2
   259  			needle := (floats[midpoint] + floats[midpoint+1]) / 2
   260  			b.ResetTimer()
   261  			for i := 0; i < b.N; i++ {
   262  				BinarySearch(floats, needle)
   263  			}
   264  		})
   265  	}
   266  }
   267  
   268  func BenchmarkBinarySearchFuncStruct(b *testing.B) {
   269  	for _, size := range []int{16, 32, 64, 128, 512, 1024} {
   270  		b.Run(fmt.Sprintf("Size%d", size), func(b *testing.B) {
   271  			structs := make([]*myStruct, size)
   272  			for i := range structs {
   273  				structs[i] = &myStruct{n: i}
   274  			}
   275  			midpoint := len(structs) / 2
   276  			needle := &myStruct{n: (structs[midpoint].n + structs[midpoint+1].n) / 2}
   277  			lessFunc := func(a, b *myStruct) int { return a.n - b.n }
   278  			b.ResetTimer()
   279  			for i := 0; i < b.N; i++ {
   280  				BinarySearchFunc(structs, needle, lessFunc)
   281  			}
   282  		})
   283  	}
   284  }