测试数据:单协程操作1亿数据,以及多协程(10条协程)操作1亿数据(每条协程操作1kw数据)
废话少说,贴代码:
单协程测试运算:
package main import ( "fmt" "time" ) func testNum(num int) { for i := 1; i <= 10000000; i++{ num = num + i num = num - i num = num * i num = num / i } } func main() { start := time.Now() for i := 1; i <= 10; i++ { testNum(1) } end := time.Now() fmt.Println(end.Sub(start).Seconds()) }
运行时间为:0.065330877
多协程测试运算:
package main import ( "fmt" "time" "sync" ) var synWait sync.WaitGroup func testNum(num int) { for i := 1; i <= 10000000; i++{ num = num + i num = num - i num = num * i num = num / i } synWait.Done() // 相当于 synWait.Add(-1) } func main() { start := time.Now() for i := 1; i <= 10; i++ { synWait.Add(1) go testNum(1) } synWait.Wait() end := time.Now() fmt.Println(end.Sub(start).Seconds()) }
运行时间为:0.019804929
比较结果,和预期的是一样,多协程要比单协程处理数据快,很多人还会去设置runtime.GOMAXPROCS(x),其实
这是远古程序员的做法了,因为go 1.6以上的版本就已经会自动根据计算机核的调用啦!!!
如果没有调用runtime.GOMAXPROCS 去设置CPU,Golang默认使用所有的cpu核。
以下是以map来做实验,为了测试准确性,统一都加锁
单协程/多协程测试map:
package main import ( "fmt" "time" "sync" ) var synWait sync.WaitGroup var normalMap map[int]int var synMutex sync.Mutex func testNum(num int) { for i := 1; i <= 10000000; i++{ synMutex.Lock() normalMap[i] = num synMutex.Unlock() } synWait.Done() // 相当于 synWait.Add(-1) } func main() { normalMap = make(map[int]int) start := time.Now() for i := 1; i <= 10; i++ { synWait.Add(1) testNum(1) // 单协程操作 //go testNum(1) // 多协程并发操作 } synWait.Wait() end := time.Now() fmt.Println(end.Sub(start).Seconds()) }
单协程操作 testNum(1), 运行时间为:19.101255922
多协程操作 go testNum(1), 运行时间为:28.210580532
是不是出乎意料!!! 多协程操作map反而慢,这说明map这个数据结构对并发操作效率比较低,如果在保证线性安全的前提下
尽量单协程去操作map,如果上面代码注释掉加锁,单协程操作就更快了, 运行时间为:16.307839364
协程通道测试map:
package main import ( "fmt" "time" "sync" ) var synWait sync.WaitGroup var normalMap map[int]int func testNum(data chan int, num int) { for i:=1;i<=10000000;i++{ data <- i } synWait.Done() } func main() { normalMap = make(map[int]int) data := make(chan int) start := time.Now() go concurrent(data) for i := 1; i <= 10; i++ { synWait.Add(1) go testNum(data,1) // 多协程并发操作 } synWait.Wait() end := time.Now() fmt.Println(end.Sub(start).Seconds()) } func concurrent(data chan int) { for { i := <- data normalMap[i] = i } }
运行时间为:53.554329275
通道内部实现也是加锁,这肯定是要比纯用锁慢一点的,这也正好验证了(网上有些人说通道要比加锁快,这是错误的)。但是使用通道是golang的一种哲学意义,规定了入口,里面的数据
结构就不要再担忧,是否要加锁了,因为全部都是安全的(可以避免很多bug,毕竟程序大部分问题还是出自程序员的逻辑代码),还是那句话不要通过共享内存来通信,而要通过通信来共享内存!
总结一下吧:(map性能 单协程 > 多协程 > 通道 )
多协程去运算确实快比单协程要快,因为golang会默认根据多核去跑,但是操作map的时候,就要注意,map并发操作效率不及单协程(有点违背多核的感觉)。通道又比纯加锁要慢。
原文地址:https://www.cnblogs.com/huangliang-hb/p/10173175.html