为什么这个Go程序运行速度这么慢?
2019-10-15 3:9:22
收藏:0
阅读:94
评论:2
我刚刚看了一些Go的简短教程并且写了一个简单的筛法程序sieve。Sieve使用筛法算法打印小于10000的所有质数,创建了很多go协程。程序的结果是正确的,但是非常慢(在我的机器上需要5秒)。我还写了实现相同算法的lua脚本和python脚本,并且运行速度要快得多(在我的机器上都约为1秒)。
需要注意的是,目的是通过比较其他语言中的协程(例如lua)和Go协程的性能来了解Go协程的性能。实现非常低效,一些评论指出这不是实现“ 埃拉托斯特尼筛法”的正确方式。是的,这是有意的。一些其他回复指出慢是由于打印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()
点赞
用户1256452
大部分时间花在了 fmt.Printf 上。
如果去掉这一行:
fmt.Printf("id: %d, number: %d, myprime: %d, number mod myprime: %d\n", sieve.id, number, myprime, number%myprime)
在我进行测试的一个例子中,运行时间从 ~5.4 秒降至 ~0.64 秒。
去掉不必要的 sync.WaitGroup,可以将时间进一步缩短到大约 ~0.48 秒。在此处查看没有 sync.WaitGroup 的版本。 你仍然需要进行大量的通道操作,而具有 yield-value-from-coroutine 操作符的语言则不需要(虽然它们有自己的问题)。这种方式不是实现素性测试的好方法。
2019-10-14 04:44:00
评论区的留言会收到邮件通知哦~
推荐文章
- Lua 虚拟机加密load(string.dump(function)) 后执行失败问题如何解决
- 我想创建一个 Nginx 规则,禁止访问
- 如何将两个不同的lua文件合成一个 东西有点长 大佬请耐心看完 我是小白研究几天了都没搞定
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?

无意义的微基准测试会产生无意义的结果。
你正在计时打印 I/O。
你正在为少量工作产生 go 协程和通道的开销。
下面是一个 Go 中的质数筛程序。输出如下:
$ go version go version devel +46be01f4e0 Sun Oct 13 01:48:30 2019 +0000 linux/amd64 $ go build sumprimes.go && time ./sumprimes 5736396 29.96µs real 0m0.001s user 0m0.001s sys 0m0.000ssumprimes.go:package main import ( "fmt" "time" ) const ( prime = 0x00 notprime = 0xFF ) func oddPrimes(n uint64) (sieve []uint8) { sieve = make([]uint8, (n+1)/2) sieve[0] = notprime p := uint64(3) for i := p * p; i <= n; i = p * p { for j := i; j <= n; j += 2 * p { sieve[j/2] = notprime } for p += 2; sieve[p/2] == notprime; p += 2 { } } return sieve } func sumPrimes(n uint64) uint64 { sum := uint64(0) if n >= 2 { sum += 2 } for i, p := range oddPrimes(n) { if p == prime { sum += 2*uint64(i) + 1 } } return sum } func main() { start := time.Now() var n uint64 = 10000 sum := sumPrimes(n) fmt.Println(sum) fmt.Println(time.Since(start)) }