【发布时间】:2019-10-15 11:09:21
【问题描述】:
我刚刚阅读了一些 Go 的简短教程并编写了一个简单的程序筛。 Sieve 使用 sieve 算法打印所有小于 10000 的素数,从而创建了很多 go 例程。我得到了正确的结果,但程序很慢(在我的机器上 5 秒)。我还编写了 lua 脚本和 python 脚本,它们实现了相同的算法,并且运行速度更快(在我的机器上都大约 1 秒)。
请注意,目的是了解 goroutine 的性能与其他语言中的协程相比,例如 lua。实施效率非常低,一些 cmets 指出实施埃拉托色尼筛法的方法不正确。是的,这是故意的。其他一些回复指出,缓慢是由打印 I/O 引起的。所以我注释掉了打印行。
我的问题是为什么我用 Go 实现的筛子程序这么慢? 代码如下:
package main
import (
"fmt"
"sync"
)
type Sieve struct {
id int;
msg_queue chan int;
wg *sync.WaitGroup;
}
func NewSieve(id int) *Sieve {
sieve := new(Sieve)
sieve.id = id
sieve.msg_queue = make(chan int)
sieve.wg = new(sync.WaitGroup)
sieve.wg.Add(1)
return sieve
}
func (sieve *Sieve) run() {
defer sieve.wg.Done()
myprime := <-sieve.msg_queue
if myprime == 0 {
return
}
// fmt.Printf("Sieve (%d) is for prime number %d.\n", sieve.id, myprime)
next_sieve := NewSieve(sieve.id + 1)
go next_sieve.run()
for {
number := <-sieve.msg_queue
if number == 0 {
next_sieve.msg_queue <- number;
next_sieve.wg.Wait()
return
} else if number % myprime != 0 {
// fmt.Printf("id: %d, number: %d, myprime: %d, number mod myprime: %d\n", sieve.id, number, myprime, number % myprime)
next_sieve.msg_queue <- number
}
}
}
func driver() {
first := NewSieve(2)
go first.run()
for n := 2; n <= 10000; n++ {
first.msg_queue <- n
}
first.msg_queue <- 0
first.wg.Wait()
}
func main() {
driver()
}
作为对比,这里是sieve.lua的代码
function sieve(id)
local myprime = coroutine.yield()
// print(string.format("Sieve (%d) is for prime number %d", id, myprime))
local next_sieve = coroutine.create(sieve)
coroutine.resume(next_sieve, id + 1)
while true do
local number = coroutine.yield()
if number % myprime ~= 0 then
// print(string.format("id: %d, number: %d, myprime: %d, number mod myprime: %d", id, number, myprime, number % myprime))
coroutine.resume(next_sieve, number)
end
end
end
function driver()
local first = coroutine.create(sieve)
coroutine.resume(first, 2)
local n
for n = 2, 10000 do
coroutine.resume(first, n)
end
end
driver()
【问题讨论】:
-
顺便说一句,this is not the Sieve of Eratosthenes。它通常被误认为是埃拉托色尼筛法,但它实际上是试除法,并且是一种特别低效的试除法形式,因为即使它通过了数字的平方根,它也会继续测试除数。
-
如果您删除
fmt.Print语句,您的代码几乎会立即运行。 -
感谢您指出此实现的效率问题。但这是故意的。我想看的是 goroutine 与其他语言的协程相比的表现。
-
我会稍微编辑一下这个问题。谢谢大家。