sort.Sort と sort.Slice の速度比較

Go 言語の標準パッケージ sort には、従来からある sort.Sort 系の関数と、1.8 から追加された sort.Slice 系の関数があります。

それぞれを使った場合、コードとしては次のように記述することになります。

```go
// 従来の sort.Sort
type ByValue []string
func (s ByValue) Len() int           { return len(s) }
func (s ByValue) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s ByValue) Less(i, j int) bool { return s[i] < s[j] }
var sslice []string = ...
sort.Sort(ByValue(sslice))

// sort.Slice
var sslice []string = ...
sort.Slice(sslice, func(i, j int) bool { return sslice[i] < sslice[j] })
```

スライスを対象にソートするのであれば、sort.Slice のほうが簡単そうです。

しかし、そうした場合、両者に速度差はあるのでしょうか？

# 結論: どっちでもよい

以下は、スライスの初期化を含むベンチマーク（単位あたりの処理速度）です。

| 方式       | 10        | 100        | 1,000        | 10,000        |
|------------|-----------|------------|--------------|---------------|
| sort.Sort  | 385 ns/op | 7145 ns/op | 156903 ns/op | 2274963 ns/op |
| sort.Slice | 423 ns/op | 7095 ns/op | 149799 ns/op | 2136996 ns/op |

大差ないので、好きな方を使いましょう。

上では処理速度のみを掲載しましたが、メモリの使用度合いも大差ありませんでした。

# ベンチマーク

込み入った比較や統計はせず、単純に3回実行してその中央値っぽい回の結果を使っています。


```:10要素
>go test -bench=. -benchmem ./...
goos: windows
goarch: amd64
pkg: my/sorthikaku
BenchmarkOldStyleSort-4          5000000               385 ns/op             192 B/op          2 allocs/op
BenchmarkSortSlice-4             3000000               423 ns/op             224 B/op          3 allocs/op
PASS
ok      my/sorthikaku   4.138s
```

```:100要素
BenchmarkOldStyleSort-4           200000              7145 ns/op            1824 B/op          2 allocs/op
BenchmarkSortSlice-4              200000              7095 ns/op            1856 B/op          3 allocs/op
```

```:1,000要素
BenchmarkOldStyleSort-4            10000            156903 ns/op           16416 B/op          2 allocs/op
BenchmarkSortSlice-4               10000            149799 ns/op           16448 B/op          3 allocs/op
```

```:10,000要素
BenchmarkOldStyleSort-4             1000           2274963 ns/op          163872 B/op          2 allocs/op
BenchmarkSortSlice-4                1000           2136996 ns/op          163904 B/op          3 allocs/op
```

# コード

比較に使ったコードを掲載しておきます。

```go:sort_test.go
package main

import (
	"fmt"
	"math/rand"
	"sort"
	"testing"
	"time"
)

type ByValue []string
func (s ByValue) Len() int           { return len(s) }
func (s ByValue) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s ByValue) Less(i, j int) bool { return s[i] < s[j] }

func genRamdomSeq(n int) []string {
	result := make([]string, n)

	rand.Seed(time.Now().UnixNano())
	for i := 0; i < n; i++ {
		result[i] = fmt.Sprintf("%d", rand.Int())
	}

	return result
}

const seqSize = 10000

func BenchmarkOldStyleSort(b *testing.B) {
	orig := genRamdomSeq(seqSize)
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		sslice := make([]string, len(orig))
		copy(sslice, orig)

		sort.Sort(ByValue(sslice))
	}
}

func BenchmarkSortSlice(b *testing.B) {
	orig := genRamdomSeq(seqSize)
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		sslice := make([]string, len(orig))
		copy(sslice, orig)

		sort.Slice(sslice, func(i, j int) bool { return sslice[i] < sslice[j] })
	}
}
```

# 最後に

sort.Slice は内部で reflect を使っているところがありますが、それだけ聞くと、sort.Slice の方が遅いのかなという印象がありました。
ですが、速度差が殆ど無いとなると、気兼ねなく使っていけそうですね。