作者 主題: [Golang] Test & Benchmark 的寫法  (閱讀 1149 次)

0 會員 與 1 訪客 正在閱讀本文。

Yamaka

  • 俺是博士!
  • *****
  • 文章數: 4904
    • 檢視個人資料
    • http://www.ecmagic.com
[Golang] Test & Benchmark 的寫法
« 於: 2015-08-10 17:39 »
go 提供的 test 與 benchmark 有3個限制,必需要依照這3個規則來做,這2兩個功能才跑得出來

1. 原始碼檔名必須以『 _b_test.go 』結尾,例如 getData_b_test.go

2. 測試的函數名必須 Test 或 Benchmark 開頭,至於之後的底線則可有可無,例如

   func TestgetData1(t *testing.T) {}
   func Test_getData1(t *testing.T) {}
   func BenchmarkgetData1(b *testing.B) {}
   func Benchmark_getData1(b *testing.B) {}

3. 如果要測試整個資料夾內的原始碼,則此資料夾的原始碼不能包含有任何的 main() 函數
   如果資料夾裡有 main() 則可改用單一檔案測試

假設現在要開發一個字串串接的功能,go 有3種方式可以串接字串
於是我們寫個測試碼來看看哪一種效率較高

src/strconcat/strconcat.go
(這個是我新開發的功能,不是測試碼,所以檔名不用加 _b_test)

代碼: [選擇]
package strconcat

import (
  "bytes"
)

func Strconcat1(str1, str2 string) string {
  return str1 + str2
}

func Strconcat12(str1, str2 string) {
  str1 += str2
}

func Strconcat13(str1, str2 string, str0 *string) {
  *str0 = str1 + str2
}

func Strconcat2(str1, str2 string, str0 *[]byte) {
  *str0 = []byte(str1)
  *str0 = append(*str0, str2...)
}

func Strconcat3(str1, str2 string, str0 *bytes.Buffer) {
  str0.WriteString(str1)
  str0.WriteString(str2)
}

接下來就要寫一個測試碼來跑這幾個函數

strconcat_b_test.go
(這個測試碼我放在目前工作資料夾內,不需要放在 src 裡)

代碼: [選擇]
package bmStrConcat

import (
  "bytes"
  "fmt"
  "testing"
  "strconcat"
)

var str1 string = "123456789012345678901234567890"
var str2 string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

func TestStrConcat1(t *testing.T) {
  str := strconcat.Strconcat1(str1, str2)
  if str != "" {
    fmt.Println(str)
  }
}

func TestStrConcat13(t *testing.T) {
  str := ""
  strconcat.Strconcat13(str1, str2, &str)
  if str != "" {
    fmt.Println(str)
  }
}

func TestStrConcat2(t *testing.T) {
  var str []byte
  strconcat.Strconcat2(str1, str2, &str)
  if string(str) != "" {
    fmt.Println(string(str))
  }
}

func TestStrConcat3(t *testing.T) {
  var str bytes.Buffer
  strconcat.Strconcat3(str1, str2, &str)
  if str.Len() >0 {
    fmt.Println(str.String())
  }
}

func BenchmarkStrConcat1(b *testing.B) {
  str0 := ""
  for idx := 0; idx < b.N; idx++ {
    str0 = strconcat.Strconcat1(str1, str2)
  }
  fmt.Sprintf("%v", str0)
}

func BenchmarkStrConcat12(b *testing.B) {
  for idx := 0; idx < b.N; idx++ {
    strconcat.Strconcat12(str1, str2)
  }
}

func BenchmarkStrConcat13(b *testing.B) {
  str0 := ""
  for idx := 0; idx < b.N; idx++ {
    strconcat.Strconcat13(str1, str2, &str0)
  }
}

func BenchmarkStrConcat2(b *testing.B) {
  var str0 []byte
  for idx := 0; idx < b.N; idx++ {
    strconcat.Strconcat2(str1, str2, &str0)
  }
}

func BenchmarkStrConcat3(b *testing.B) {
  var str0 bytes.Buffer
  for idx := 0; idx < b.N; idx++ {
    str0.Reset()
    strconcat.Strconcat3(str1, str2, &str0)
  }
}


Testxxxxx 的部分主要是查看新函數跑出來的結果是否正確
不一定要像上面那樣寫,也可以改成這樣

代碼: [選擇]
var str3 string = str1 + str2

func TestStrConcat1(t *testing.T) {
  str := strconcat.Strconcat1(str1, str2)
  if str != "" && str == str3 {
    fmt.Println("1 OK!")
  }
}

func TestStrConcat13(t *testing.T) {
  str := ""
  strconcat.Strconcat13(str1, str2, &str)
  if str != "" && str == str3 {
    fmt.Println("13 OK!")
  }
}

func TestStrConcat2(t *testing.T) {
  var str []byte
  strconcat.Strconcat2(str1, str2, &str)
  if string(str) == str3 {
    fmt.Println("2 OK!")
  }
}

func TestStrConcat3(t *testing.T) {
  var str bytes.Buffer
  strconcat.Strconcat3(str1, str2, &str)
  if str.String() == str3 {
    fmt.Println("3 OK!")
  }
}


Benchmark 的部分就是用迴圈去呼叫新函數...

執行方式跟原來差不多,只是 run 改成 test,並加個選項

引用
$ go test strconcat_b_test.go -bench=.
1 OK!
13 OK!
2 OK!
3 OK!
PASS
BenchmarkStrConcat1-4    10000000          205 ns/op
BenchmarkStrConcat12-4   10000000          218 ns/op
BenchmarkStrConcat13-4   10000000          225 ns/op
BenchmarkStrConcat2-4     5000000          331 ns/op
BenchmarkStrConcat3-4    30000000           56.4 ns/op
ok     command-line-arguments   10.918s

-bench=. 表示測試所有的 Textxxx() 與 Benchmarkxxx()

有時候只想看某部分的結果,例如只跑 StrConcat3 的部分,指令可以這樣下

引用
$ go test strconcat_b_test.go -run=.*t3 -bench=.*t3
3 OK!
PASS
BenchmarkStrConcat3-4   30000000           52.4 ns/op
ok     command-line-arguments   1.642s

跑 StrConcat2 跟 StrConcat3 的話

引用
$ go test strconcat_b_test.go -run=.*t[23] -bench=.*t[23]
2 OK!
3 OK!
PASS
BenchmarkStrConcat2-4    5000000          315 ns/op
BenchmarkStrConcat3-4   30000000           52.4 ns/op
ok     command-line-arguments   3.551s

或是跑 1,3

引用
$ go test strconcat_b_test.go -run=.*t[13] -bench=.*t[13]
1 OK!
13 OK!
3 OK!
PASS
BenchmarkStrConcat1-4    10000000          200 ns/op
BenchmarkStrConcat12-4   10000000          190 ns/op
BenchmarkStrConcat13-4   10000000          199 ns/op
BenchmarkStrConcat3-4    30000000           51.7 ns/op
ok     command-line-arguments   8.193s

or

引用
$ go test strconcat_b_test.go -run=.*t[13]$ -bench=.*t[13]$
1 OK!
3 OK!
PASS
BenchmarkStrConcat1-4   10000000          199 ns/op
BenchmarkStrConcat3-4   30000000           52.1 ns/op
ok     command-line-arguments   3.834s

t[13]$ 表示結尾,所以 strconcat12, strconcat13 就不會跑


有人說,自己寫 package 之後一定要加 test 的碼
其實看個人啦,如果是要公開給大家用的,那當然就需要囉
最好連 benchmark 的部分都直接提供
如果只是自己用,那就看自己嚕!^_____^