nightingale/vendor/github.com/twotwotwo/sorts/parallel.go

82 lines
1.7 KiB
Go

package sorts
import (
"runtime"
"sort"
"sync"
)
// helpers to coordinate parallel sorts
type sortFunc func(sort.Interface, task, func(task))
// MaxProcs controls how many goroutines to start for large sorts. If 0,
// GOMAXPROCS will be used; if 1, all sorts will be serial.
var MaxProcs = 0
// minParallel is the size of the smallest collection we will try to sort in
// parallel.
var minParallel = 10000
// minOffload is the size of the smallest range that can be offloaded to
// another goroutine.
var minOffload = 127
// bufferRatio is how many sorting tasks to queue (buffer) up per
// worker goroutine.
var bufferRatio float32 = 1
// parallelSort calls the sorters with an asyncSort function that will hand
// the task off to another goroutine when possible.
func parallelSort(data sort.Interface, sorter sortFunc, initialTask task) {
max := runtime.GOMAXPROCS(0)
if MaxProcs > 0 && MaxProcs < max {
max = MaxProcs
}
l := data.Len()
if l < minParallel {
max = 1
}
var syncSort func(t task)
syncSort = func(t task) {
sorter(data, t, syncSort)
}
if max == 1 {
syncSort(initialTask)
return
}
wg := new(sync.WaitGroup)
// buffer up one extra task to keep each cpu busy
sorts := make(chan task, int(float32(max)*bufferRatio))
var asyncSort func(t task)
asyncSort = func(t task) {
if t.end-t.pos < minOffload {
sorter(data, t, syncSort)
return
}
wg.Add(1)
select {
case sorts <- t:
default:
sorter(data, t, asyncSort)
wg.Done()
}
}
doSortWork := func() {
for task := range sorts {
sorter(data, task, asyncSort)
wg.Done()
}
}
for i := 0; i < max; i++ {
go doSortWork()
}
asyncSort(initialTask)
wg.Wait()
close(sorts)
}