Go藉助PProf的一次效能優化

Gundy發表於2019-03-25

本文以leetcode的一題為例來講解如何通過PProf來優化我們的程式,題目如下:Longest Substring Without Repeating Characters

首先給出我們一般的解法

func lengthOfNonRepeatingSubStr(s string) int {
	lastOccurred := make(map[rune]int)
	start := 0
	maxLength := 0

	for i, ch := range []rune(s) {
		if lastI, ok := lastOccurred[ch]; ok && lastI >= start {
			start = lastI + 1
		}
		if i-start+1 > maxLength {
			maxLength = i - start + 1
		}
		lastOccurred[ch] = i
	} 

	return maxLength
}
複製程式碼

效能檢測可知 200次, 花費時間 6970790 ns/op

Go藉助PProf的一次效能優化

PProf分析

go test -bench . -cpuprofile cpu.out   //首先生成cpuprofile檔案

go tool pprof -http=:8080 ./啟動 PProf 視覺化介面 分析cpu.out檔案

複製程式碼

通過訪問 http://localhost:8080/ui/ 可以檢視到如下頁面

Go藉助PProf的一次效能優化

可以看到主要消耗時間在2大塊,一個是mapaccess,mapassign,還有一塊是decoderune。 decoderune主要是對UFT8字元的解碼,將字串轉換成 []rune(s) 這個是不能避免的。所以主要去解決map的訪問和賦值問題,也就是程式碼中的lastOccurred

優化分析

由於map需要進行算hash 判重,分配空間等操作會導致操作慢下來,解決思路就是用空間換時間,通過slice來替換map.

修改後的程式碼如下:

func lengthOfNonRepeatingSubStr2(s string) int {
	lastOccurred := make([]int, 0xffff)
	//賦給一個初始值
	for i := range lastOccurred {
		lastOccurred[i] = -1
	}
	start := 0
	maxLength := 0

	for i, ch := range []rune(s) {
		if lastI := lastOccurred[ch]; lastI != -1 && lastI >= start {
			start = lastI + 1
		}
		if i-start+1 > maxLength {
			maxLength = i - start + 1
		}
		lastOccurred[ch] = i
	}

	return maxLength
}
複製程式碼

效能檢測可知 500, 花費時間 2578859 ns/op。 相比之前的6970790 ns/op 已經又很大的優化了

Go藉助PProf的一次效能優化

後續優化

通過pprof檢視除了decoderune外還有一部分時間花費在makeslice上面,這是由於每次呼叫函式都要makeslicke,可以slice移到函式外面進行宣告。具體可以自己操作下,然後檢視下pprof圖上面的makeslice是否有消除。

Go藉助PProf的一次效能優化

PProf具使用可檢視 Golang 大殺器之效能剖析 PProf

本文亦在微信公眾號【小道資訊】釋出,歡迎掃碼關注!

Go藉助PProf的一次效能優化

相關文章