【发布时间】:2017-11-29 23:08:26
【问题描述】:
所以我确实有一个由我编写的并发快速排序实现。它看起来像这样:
func Partition(A []int, p int, r int) int {
index := MedianOf3(A, p, r)
swapArray(A, index, r)
x := A[r]
j := p - 1
i := p
for i < r {
if A[i] <= x {
j++
tmp := A[j]
A[j] = A[i]
A[i] = tmp
}
i++
}
swapArray(A, j+1, r)
return j + 1
}
func ConcurrentQuicksort(A []int, p int, r int) {
wg := sync.WaitGroup{}
if p < r {
q := Partition(A, p, r)
select {
case sem <- true:
wg.Add(1)
go func() {
ConcurrentQuicksort(A, p, q-1)
<-sem
wg.Done()
}()
default:
Quicksort(A, p, q-1)
}
select {
case sem <- true:
wg.Add(1)
go func() {
ConcurrentQuicksort(A, q+1, r)
<-sem
wg.Done()
}()
default:
Quicksort(A, q+1, r)
}
}
wg.Wait()
}
func Quicksort(A []int, p int, r int) {
if p < r {
q := Partition(A, p, r)
Quicksort(A, p, q-1)
Quicksort(A, q+1, r)
}
}
我有一个 sem 缓冲通道,我用它来限制运行的 goroutine 的数量(如果它达到这个数量,我不会设置另一个 goroutine,我只是对子数组进行正常的快速排序)。首先我从 100 开始,然后我更改为 50、20。基准会稍微好一些。但切换到10后,它开始倒退,时间开始变大。因此,至少对于我的硬件而言,有一些任意数字使算法运行效率最高。
当我实现这个时,我实际上看到了一些关于最好的 goroutine 数量的问题,现在我找不到它(愚蠢的 Chrome 历史实际上并没有保存所有访问过的网站)。你知道如何计算这样的事情吗?如果我不必对其进行硬编码,那将是最好的,只需让程序自己完成即可。
P.S 我有非并发快速排序,它的运行速度比这慢大约 1.7 倍。正如您在我的代码中看到的那样,当运行的 goroutine 的数量超过我之前设置的数量时,我会执行 Quicksort。我想使用ConcurrentQuicksort 怎么样,但不使用go 关键字调用它,只是简单地调用它,也许如果其他goroutine 完成它们的工作,我调用的ConcurrentQuicksort 将开始启动goroutines,加速这个过程(因为你可以看到Quicksort 只会启动递归快速排序,没有 goroutines)。我这样做了,实际上时间比常规快速排序慢了 10%。你知道为什么会这样吗?
【问题讨论】:
-
我玩过并发排序 (github.com/twotwotwo/sorts)。我认为这里的第一原则没有明确的答案。您只需观察转换成本是多少并调整工作单元的大小,这样您就不会花费太多的资源来处理任务。
-
好的,谢谢。你知道我在 PS 部分放的东西吗?
-
这可能是stackoverflow.com/q/8509152/4639336的可能副本
-
@reticentroot 因为问题是什么影响并行快速排序的效率,而不是 Go 运行时的限制,可以说是不同的
-
因此可能不是我的直接标志,尽管它确实显示了一些有趣的代码来对 goroutines 进行基准测试......另外你是如何进行基准测试的?希望通过 go test =bench 运行一些函数,如果没有看到这篇文章 dave.cheney.net/2013/06/30/how-to-write-benchmarks-in-go 在做你可以快速测试并找到“甜蜜”点
标签: go concurrency goroutine