golang 性能測試 (1) 基準性能測試

  • 2020 年 4 月 13 日
  • 筆記

本文介紹golang 如何做基準性能測試。

編寫完程式碼除了跑必要的單元測試外,還需要考慮程式碼跑起來的性能如何。性能的衡量其實就是程式運行時候進程的記憶體分配,CPU消耗情況。

golang 語言在提供了功能測試的基礎上,提供了豐富的性能測試功能。

SHOW CODE

首先,從一個例子來講起。 隨便寫一個簡單的快速排序,然後和系統自帶的排序做一個性能比較。

如下為簡版快排的程式碼:

package benchmark

import "sort"

func QSort(data []int) {
	myqsort(data, 0, len(data)-1)
}

func myqsort(data []int, s, e int) {
	if s >= e {
		return
	}

	t := data[s]
	i, j := s, e

	for i < j {
		for ; i < j && data[j] >= t; j-- { }
		for ; i < j && data[i] < t; i++ { }
		if i < j { break }

		data[i], data[j] = data[j], data[i]
		i++
		j--
	}

	data[i] = t
	myqsort(data, s, i-1)
	myqsort(data, i+1, e)
}

然後編寫一個測試的test。

package benchmark

import "testing"
import "math/rand"
import "time"
import "sort"

var ints []int

// 長度為 1w 的數據使用系統自帶排序
func BenchmarkSort10k(t *testing.B) {
	slice := ints[0:10000]
	t.ResetTimer()   // 只考慮下面程式碼的運行事件,所以重置計時器
	for i := 0; i < t.N; i++ {
		sort.Ints(slice)
	}
}

// 長度為 100 的數據使用系統自帶排序
func BenchmarkSort100(t *testing.B) {
	slice := ints[0:100]
	t.ResetTimer()
	for i := 0; i < t.N; i++ {
		sort.Ints(slice)
	}
}

// 長度為 1w 的數據使用上述程式碼排序
func BenchmarkQsort10k(t *testing.B) {
	slice := ints[0:10000]
	t.ResetTimer()
	for i := 0; i < t.N; i++ {
		QSort(slice)
	}
}

// 長度為 100 的數據使用上述程式碼排序
func BenchmarkQsort100(t *testing.B) {
	slice := ints[0:100]
	t.ResetTimer()
	for i := 0; i < t.N; i++ {
		QSort(slice)
	}
}

// 數據初始化,為了保證每次數據都是一致的。
func TestMain(m *testing.M) {
	rand.Seed(time.Now().Unix())
	ints = make([]int, 10000)

	for i := 0; i < 10000; i++ {
		ints[i] = rand.Int()
	}

	m.Run()
}

運行命令 :

# go test -cover -count 3  -benchmem  -bench=.

運行結果如下圖:

基準測試,默認將每個方法執行1s中,然後展示執行的次數,每一次執行的耗時, 上述還展示了記憶體每次分配的大小,以及每次benchmark分配的次數。上述的命令行指定了運行次數為3次,顯示程式碼覆蓋率和記憶體分配情況。

從基準測試的結果可以分析出:對於1w數據量的排序,自帶的排序比我的排序演算法要快20倍左右;100數據量的排序,手擼的排序略勝一籌。
從記憶體分析來講,系統自帶的會使用4B的數據,而我的演算法無記憶體分配。

INTRODUCE BENCHMARK

引入golang 提供的 testing 包,寫需要的基準測試的方法(方法名必須以Benchmark開頭, 參數必須為 *testing.B)。

若需要做一些數據初始化的工作,可以如上寫一個TestMain 方法,將數據初始化的工作在這裡完成。

除了這些,可以看*testing.B, *testing.M 的相關方法即可。

最後,只要運行官方提供的 go test -bench=. 命令,即可開始跑基準測試。 當然,還有其他選項可以滿足我們多樣的需求。
例如:

  • -cpu 1,2,4 指定運行的cpu 格式
  • -count n 指定運行的次數
  • -benchtime 每一條測試執行的時間 (默認是1s)
  • -bench 指定執行bench的方法, . 是全部
  • -benchmem 顯示記憶體分配情況

其他參數可以通過 go help testflag 查看

WHY SO SLOW

  1. 我這裡選取的是第一個數作為中位數,數據越大越可能出現傾斜,排序慢的概率也大。
  2. 正常的排序包中,都會在對小於等於12 個數的數組做排序時使用希爾排序,速度也有很大提升。

除了簡單的做性能測試外,golang 還自帶了性能分析的工具,我們可以快速找出程式碼中的記憶體分配、cpu消耗的核心區,幫助我們解決服務的性能問題。下篇文章將做詳細了解。