sort.Sort() vs slices.Sort()
Asked Answered
C

1

6

Given a []int, e.g: is := []int{2, 4, 1, 3}

Can sort via:

  1. sort.Sort(), e.g:
    sort.Sort(sort.IntSlice(is))
    
  2. slices.Sort(), e,g:
    slices.Sort(is)
    

I know slices.Sort() is experience, from "golang.org/x/exp/slices".
Other than that, is there any difference when sort an []int, and which is preferred usually?

Corbett answered 2/4, 2023 at 7:38 Comment(1)
To sort []int use neither but sort.Ints.Satirist
C
3

slices are no longer experimental, so the question is very much valid for the current Go. I've tried the following:

import (
        "cmp"
        "math/rand/v2"
        "slices"
        "sort"
        "testing"
)

const (
        seed1       = 42
        seed2       = 100_500
        numElements = 100_000
)

func BenchmarkSortInts(b *testing.B) {
        var (
                random = rand.New(rand.NewPCG(seed1, seed2)) // static seeds for reproducible results                                                                                                                                                                                                                                                                                        
                arr    = random.Perm(numElements)
                arrcp  = slices.Clone(arr)
        )

        b.Run("sort.Ints", func(b *testing.B) {
                for range b.N {
                        b.StopTimer()
                        copy(arrcp, arr)
                        b.StartTimer()
                        sort.Ints(arrcp)
                }
        })
        b.Run("sort.Sort", func(b *testing.B) {
                for range b.N {
                        b.StopTimer()
                        copy(arrcp, arr)
                        b.StartTimer()
                        sort.Sort(sort.IntSlice(arrcp))
                }
        })
        b.Run("sort.Slice", func(b *testing.B) {
                for range b.N {
                        b.StopTimer()
                        copy(arrcp, arr)
                        b.StartTimer()
                        sort.Slice(arrcp, func(i, j int) bool {
                                return arrcp[i] < arrcp[j]
                        })
                }
        })
        b.Run("slices.Sort", func(b *testing.B) {
                for range b.N {
                        b.StopTimer()
                        copy(arrcp, arr)
                        b.StartTimer()
                        slices.Sort(arrcp)
                }
        })
        b.Run("slices.SortFunc", func(b *testing.B) {
                for range b.N {
                        b.StopTimer()
                        copy(arrcp, arr)
                        b.StartTimer()
                        slices.SortFunc(arrcp, cmp.Compare)
                }
        })
        b.Run("slices.SortCustomFunc", func(b *testing.B) {
                for range b.N {
                        b.StopTimer()
                        copy(arrcp, arr)
                        b.StartTimer()
                        slices.SortFunc(arrcp, func(a, b int) int { return a - b })
                }
        })
}

And it gave me these results (Go 1.23, averaged over 10 runs):

goos: linux
goarch: amd64
pkg: github.com/nspcc-dev/neo-go/config
cpu: AMD Ryzen 7 PRO 7840U w/ Radeon 780M Graphics  
                                  │   sorting   │
                                  │   sec/op    │
SortInts/sort.Ints-16               4.365m ± 1%
SortInts/sort.Sort-16               7.895m ± 1%
SortInts/sort.Slice-16              7.648m ± 1%
SortInts/slices.Sort-16             4.380m ± 1%
SortInts/slices.SortFunc-16         6.846m ± 0%
SortInts/slices.SortCustomFunc-16   6.248m ± 1%
geomean                             6.057m

                                  │   sorting    │
                                  │     B/op     │
SortInts/sort.Ints-16               0.000 ± 0%
SortInts/sort.Sort-16               24.00 ± 0%
SortInts/sort.Slice-16              56.00 ± 0%
SortInts/slices.Sort-16             0.000 ± 0%
SortInts/slices.SortFunc-16         0.000 ± 0%
SortInts/slices.SortCustomFunc-16   0.000 ± 0%
geomean                                        ¹
¹ summaries must be >0 to compute geomean

                                  │   sorting    │
                                  │  allocs/op   │
SortInts/sort.Ints-16               0.000 ± 0%
SortInts/sort.Sort-16               1.000 ± 0%
SortInts/sort.Slice-16              2.000 ± 0%
SortInts/slices.Sort-16             0.000 ± 0%
SortInts/slices.SortFunc-16         0.000 ± 0%
SortInts/slices.SortCustomFunc-16   0.000 ± 0%
geomean                                        ¹
¹ summaries must be >0 to compute geomean

sort.Ints in fact just calls slices.Sort now (1.22+) internally (see https://pkg.go.dev/sort#Ints) and the result just proves it. All the other ways incur noticeably more overhead. So in general slices.Sort works fine when it can be used and likely slices are also a bit better now in more complex scenarios like How to sort struct with multiple sort parameters?.

Clasping answered 25/8 at 16:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.