【问题标题】:Performance: Sorting Slice vs Sorting Type (of Slice) with Sort implementation性能:使用排序实现对切片进行排序与​​(切片的)排序类型
【发布时间】:2023-03-03 02:47:01
【问题描述】:

我正在处理一些代码挑战,发现自定义排序(排序接口的实现)比仅用于切片的原始结构要快得多。这是为什么?将切片转换为类型是否会产生一些魔力(例如转换为指向结构的指针切片)?

我编写了一些代码来测试我的臀部收缩

package sortingexample

import (
    "sort"
    "testing"
)

// Example of struct we going to sort.

type Point struct {
    X, Y int
}

// --- Struct / Raw Data
var TestCases = []Point{
    {10, 3},
    {10, 4},
    {10, 35},
    {10, 5},
    {10, 51},
    {10, 25},
    {10, 59},
    {10, 15},
    {10, 22},
    {10, 91},
}

// Example One - Sorting Slice Directly
// somehow - slowest way to sort it.
func SortSlice(points []Point) {
    sort.Slice(points, func(i, j int) bool {
        return points[i].Y < points[j].Y
    })
}

func BenchmarkSlice(b *testing.B) {
    tmp := make([]Point, len(TestCases))
    for i := 0; i < b.N; i++ {
        copy(tmp, TestCases)
        SortSlice(tmp)
    }
}

// Example Two - Sorting Slice Directly
// much faster performance
type Points []Point

// Sort interface implementation
func (p Points) Less(i, j int) bool { return p[i].Y < p[j].Y }
func (p Points) Len() int           { return len(p) }
func (p Points) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

func SortStruct(points []Point) {
    sort.Sort(Points(points))
}

func BenchmarkStruct(b *testing.B) {
    tmp := make([]Point, len(TestCases))
    for i := 0; i < b.N; i++ {
        copy(tmp, TestCases)
        SortStruct(tmp)
    }
}

// --- Pointers
var TestCasesPoints = []*Point{
    &Point{10, 3},
    &Point{10, 4},
    &Point{10, 35},
    &Point{10, 5},
    &Point{10, 51},
    &Point{10, 25},
    &Point{10, 59},
    &Point{10, 15},
    &Point{10, 22},
    &Point{10, 91},
}

// Example Three - Sorting Slice of Pointers

func SortSlicePointers(points []*Point) {
    sort.Slice(points, func(i, j int) bool {
        return points[i].Y < points[j].Y
    })
}

func BenchmarkSlicePointers(b *testing.B) {
    tmp := make([]*Point, len(TestCasesPoints))
    for i := 0; i < b.N; i++ {
        copy(tmp, TestCasesPoints)
        SortSlicePointers(tmp)
    }
}

// Example Four - Sorting Struct (with Slice of pointers beneath it)
type PointsPointer []*Point

func (pp PointsPointer) Less(i, j int) bool { return pp[i].Y < pp[j].Y }
func (pp PointsPointer) Len() int           { return len(pp) }
func (pp PointsPointer) Swap(i, j int)      { pp[i], pp[j] = pp[j], pp[i] }

func SortStructOfSlicePointers(points []*Point) {
    sort.Sort(PointsPointer(points))
}

func BenchmarkStructOfSlicePointers(b *testing.B) {
    tmp := make([]*Point, len(TestCasesPoints))

    for i := 0; i < b.N; i++ {
        copy(tmp, TestCasesPoints)
        SortStructOfSlicePointers(tmp)
    }
}

这是结果...

> go test -bench=.
goos: darwin
goarch: amd64
BenchmarkSlice-4                     3000000           542 ns/op
BenchmarkStruct-4                    5000000           318 ns/op
BenchmarkSlicePointers-4             5000000           280 ns/op
BenchmarkStructOfSlicePointers-4     5000000           321 ns/op

很明显,对一片指针进行排序会更快,但为什么自定义排序实现会更快呢?有什么我可以阅读的资源吗?

【问题讨论】:

  • less 函数应该比较less,而不是小于或等于。四个测试中最快的使用 sort.Slice。
  • 感谢您的回答。 1)好的(这是一个意外)。 2)如果你不需要移动大块内存,一切都会更快,默认情况下指针更快,问题是关于[]Structs。

标签: sorting go slice


【解决方案1】:

一般的sort.Slice() 和sort.SliceStable() 函数适用于任何切片。您必须将切片值作为interface{} 值传递,并且实现必须使用反射(reflect 包)来访问其元素和长度,并执行元素交换。

相反,当您自己实现sort.Interface 类型时,在您的实现中您可以访问切片的静态类型,并且您可以提供sort.Interface 的实现而无需重新考虑,这就是它的原因更快。

因此,如果性能至关重要/重要,请始终自己提供 sort.Interface 实现。如果切片很小或者性能不重要,可以使用更方便的sort.Slice()函数。

【讨论】:

    【解决方案2】:

    使用分配添加运行输出看起来接口/结构方法也更好。

    ❯ go version
    go version go1.17.1 darwin/amd64
    ❯ go test -bench=. -benchmem
    goos: darwin
    goarch: amd64
    pkg: github.com/timescale/promscale/pkg/api/parser/json/test
    cpu: Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz
    BenchmarkSlice-12                        3533616               319.6 ns/op            88 B/op          3 allocs/op
    BenchmarkStruct-12                       9157018               126.0 ns/op            24 B/op          1 allocs/op
    BenchmarkSlicePointers-12                6643446               167.1 ns/op            56 B/op          2 allocs/op
    BenchmarkStructOfSlicePointers-12        9004021               124.1 ns/op            24 B/op          1 allocs/op
    PASS
    ok      github.com/timescale/promscale/pkg/api/parser/json/test 5.425s
    

    【讨论】:

      猜你喜欢
      • 2018-06-25
      • 2019-03-15
      • 2017-08-29
      • 1970-01-01
      • 2021-08-15
      • 2016-02-20
      • 2019-01-08
      • 2021-03-14
      • 1970-01-01
      相关资源
      最近更新 更多