golang 單協程和多協程的性能測試

測試數據:單協程操做1億數據,以及多協程(10條協程)操做1億數據(每條協程操做1kw數據)html

廢話少說,貼代碼:程序員

單協程測試運算:golang

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),其實app

這是遠古程序員的作法了,由於go 1.6以上的版本就已經會自動根據計算機核的調用啦!!!性能

若是沒有調用runtime.GOMAXPROCS 去設置CPU,Golang默認使用全部的cpu核測試

 

如下是以map來作實驗,爲了測試準確性,統一都加鎖spa

單協程/多協程測試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    // testslic = append(testslic, num) 這裏slice也是同樣的道理
		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

緣由爲何呢???這篇博客有所闡述:https://www.cnblogs.com/ipub520/p/7718905.html

 

協程通道測試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,畢竟程序大部分問題仍是出自程序員的邏輯代碼),仍是那句話不要經過共享內存來通訊,而要經過通訊來共享內存!

slice也是和map差很少的,併發append的時候也必須加鎖

咱們舉一個簡單例子,好比,當A和B兩個協程運行append的時候同時發現s[1]這個位置是空的,他們就都會把本身的值放在這個位置,這樣他們兩個的值就會覆蓋,形成數據丟失。

 

總結一下吧:(map性能 單協程 > 多協程 > 通道 )

多協程去運算確實快比單協程要快,由於golang會默認根據多核去跑,可是若是操做涉及到加鎖的時候(例如map,slice),就要注意,併發操做效率不及單協程(這點和erlang操做ets不同,erlang剛好相反)。

相關文章
相關標籤/搜索