Go笔记(三):测试、反射、json解析等
单元测试
- 文件 _test 结尾,方法名 Test 开头
- Fail,Error: 测试失败,该测试继续,其他测试也继续执行
- FailNow, Fatal: 该测试失败,该测试终止,其他测试继续执行
代码覆盖率
go test -v -cover
断言:
https://github.com/stretchr/testify
func TestForSqure(t *testing.T) {
inputs := [...]int{1, 2, 3}
expected := [...]int{1, 4, 9}
for i := 0; i < len(inputs); i++ {
ret := square(inputs[i])
if ret != expected[i] {
t.Errorf("input %d expected %d, the actual is %d",
inputs[i], expected[i], ret)
}
}
}
func square(i int) int {
return i * i
}
func TestErrorInCode(t *testing.T) {
fmt.Println("Start")
t.Error("Error")
fmt.Println("End")
}
func TestFailInCode(t *testing.T) {
fmt.Println("Start")
t.Fatal("Error") //该测试失败,该测试终止,其他测试继续
fmt.Println("End") //不会执行本行
}
func TestForSqure(t *testing.T) {
inputs := [...]int{1, 2, 3}
expected := [...]int{1, 4, 9}
for i := 0; i < len(inputs); i++ {
ret := square(inputs[i])
if ret != expected[i] {
t.Errorf("input %d expected %d, the actual is %d",
inputs[i], expected[i], ret)
}
}
}
func square(i int) int {
return i * i
}
func TestErrorInCode(t *testing.T) {
fmt.Println("Start")
t.Error("Error")
fmt.Println("End")
}
func TestFailInCode(t *testing.T) {
fmt.Println("Start")
t.Fatal("Error") //该测试失败,该测试终止,其他测试继续
fmt.Println("End") //不会执行本行
}
BenchNow 性能测评
运行所有文件 go test -bench=. [-benchmenm (分析内存大小,allocs 内存个数)]
运行方法 go test -bench=方法名 [-benchmenm (分析内存大小,allocs 内存个数)]
func BenchmarkXX(b *testing.B) {
//性能无关的代码
//....
b.ResetTimer()
for i := 0; i < b.N; i++ {
}
b.StopTimer()
//性能无关的代码
//...
}
func BenchmarkXX(b *testing.B) {
//性能无关的代码
//....
b.ResetTimer()
for i := 0; i < b.N; i++ {
}
b.StopTimer()
//性能无关的代码
//...
}
BDD (Behavior Driven Developement)
BDD in Go
地址: https://github.com/smartystreets/goconvey
安装:
go get -u github.com/smartystreets/goconvey
go get -u github.com/smartystreets/goconvey
启动 Web ui:
$GOPATH/bin/goconvey
反射 基础操作
反射
反射类型 reflect.TypeOf
反射值 reflect.ValueOf
- reflect.TypeOf 返回类型(reflect.Type)
- reflect.ValueOf 返回值 (reflect.Value)
- 可以从 reflect.Value 获得类型
- 通过 kind 来判断类型(枚举)
func CheckType(v interface{}) {
t := reflect.TypeOf(v)
switch t.Kind() {
case reflect.Float32, reflect.Float64:
fmt.Println("Float")
case reflect.Int, reflect.Int32, reflect.Int64:
fmt.Println("Integer")
case reflect.Ptr:
fmt.Println("Ptr")
default:
fmt.Println("other type")
}
}
func TestBaseType(t *testing.T) {
var f float64 = 1
CheckType(f) // Float
CheckType(&f) // Ptr
}
func TestTypeAndValue(t *testing.T) {
var f int64 = 10
t.Log(reflect.TypeOf(f), reflect.ValueOf(f)) // int64 10
t.Log(reflect.ValueOf(f).Type()) //int64
}
func CheckType(v interface{}) {
t := reflect.TypeOf(v)
switch t.Kind() {
case reflect.Float32, reflect.Float64:
fmt.Println("Float")
case reflect.Int, reflect.Int32, reflect.Int64:
fmt.Println("Integer")
case reflect.Ptr:
fmt.Println("Ptr")
default:
fmt.Println("other type")
}
}
func TestBaseType(t *testing.T) {
var f float64 = 1
CheckType(f) // Float
CheckType(&f) // Ptr
}
func TestTypeAndValue(t *testing.T) {
var f int64 = 10
t.Log(reflect.TypeOf(f), reflect.ValueOf(f)) // int64 10
t.Log(reflect.ValueOf(f).Type()) //int64
}
通过字符串的形式调用类型的某个方法
按名字访问结构的成员
reflect.ValueOf(*e).FieldByName("Name")
按名字访问结构的方法
reflect.ValueOf(e).MethodByName("UpdateAge").Call([]reflect.Value{reflect.ValueOf(1)})
type EmployeeT struct {
EmployeeId string
Name string `format:"normal"` //struct tag ,类似java的注解
Age int
}
func (e *EmployeeT) UpdateAge(newVal int) {
e.Age = newVal
}
func TestInvokeByName(t *testing.T) {
e := &EmployeeT{"1", "Mike", 30}
// 按名字获取成员
t.Logf("Name:value(%[1]v), Type(%[1]T)",reflect.ValueOf(*e).FieldByName("EmployeeId")) // Name:value(1), Type(reflect.Value)
if nameField, ok := reflect.TypeOf(*e).FieldByName("Name"); !ok {
t.Error("Failed to get 'name' field")
} else {
// 通过反射获取struct tag
t.Log("Tag: format", nameField.Tag.Get("format")) // Tag: format normal
}
reflect.ValueOf(e).MethodByName("UpdateAge").Call([]reflect.Value{reflect.ValueOf(99)})
t.Log("update age:", e) // update age: &{1 Mike 99} age更新为99
}
type EmployeeT struct {
EmployeeId string
Name string `format:"normal"` //struct tag ,类似java的注解
Age int
}
func (e *EmployeeT) UpdateAge(newVal int) {
e.Age = newVal
}
func TestInvokeByName(t *testing.T) {
e := &EmployeeT{"1", "Mike", 30}
// 按名字获取成员
t.Logf("Name:value(%[1]v), Type(%[1]T)",reflect.ValueOf(*e).FieldByName("EmployeeId")) // Name:value(1), Type(reflect.Value)
if nameField, ok := reflect.TypeOf(*e).FieldByName("Name"); !ok {
t.Error("Failed to get 'name' field")
} else {
// 通过反射获取struct tag
t.Log("Tag: format", nameField.Tag.Get("format")) // Tag: format normal
}
reflect.ValueOf(e).MethodByName("UpdateAge").Call([]reflect.Value{reflect.ValueOf(99)})
t.Log("update age:", e) // update age: &{1 Mike 99} age更新为99
}
反射 万能程序
主要列举几个应用反射的例子。
DeepEqual
DeepEqual 比较切片和 map
func TestDeepEqual(t *testing.T) {
a := map[int]string{1: "one", 2: "two", 3: "three"}
b := map[int]string{1: "two", 2: "two", 4: "three"}
//fmt.Println(a == b) // 编译不通过
fmt.Println(reflect.DeepEqual(a, b)) //false
s1 := []int{1, 2, 3}
s2 := []int{1, 2, 3}
s3 := []int{1, 2, 4}
t.Log("s1 == s2 ? ", reflect.DeepEqual(s1, s2)) // true
t.Log("s1 == s3 ? ", reflect.DeepEqual(s1, s3)) // false
}
func TestDeepEqual(t *testing.T) {
a := map[int]string{1: "one", 2: "two", 3: "three"}
b := map[int]string{1: "two", 2: "two", 4: "three"}
//fmt.Println(a == b) // 编译不通过
fmt.Println(reflect.DeepEqual(a, b)) //false
s1 := []int{1, 2, 3}
s2 := []int{1, 2, 3}
s3 := []int{1, 2, 4}
t.Log("s1 == s2 ? ", reflect.DeepEqual(s1, s2)) // true
t.Log("s1 == s3 ? ", reflect.DeepEqual(s1, s3)) // false
}
结构体填充
type Customer struct {
CookieID string
Name string
Age int
}
/*
结构体填充
*/
func fillBySettings(st interface{}, settings map[string]interface{}) error {
// func (v Value) Elem() Value
// Elem returns the value that the interface v contains or that the pointer v points to.
// It panics if v's Kind is not Interface or Ptr.
// It returns the zero Value if v is nil.
if reflect.TypeOf(st).Kind() != reflect.Ptr {
return errors.New("the first param should be a pointer to the struct type.")
}
// Elem() 获取指针指向的值
if reflect.TypeOf(st).Elem().Kind() != reflect.Struct {
return errors.New("the first param should be a pointer to the struct type.")
}
if settings == nil {
return errors.New("settings is nil.")
}
var (
field reflect.StructField
ok bool
)
for k, v := range settings {
// 检查类型里有没有field (因为是指针类型,需要用Elem(),获取指针类型的)
if field, ok = (reflect.ValueOf(st)).Elem().Type().FieldByName(k); !ok {
continue
}
if field.Type == reflect.TypeOf(v) {
vstr := reflect.ValueOf(st)
vstr = vstr.Elem()
vstr.FieldByName(k).Set(reflect.ValueOf(v))
}
}
return nil
}
func TestFillNameAndAge(t *testing.T) {
settings := map[string]interface{}{"Name": "Mike", "Age": 30}
e := Employee{}
if err := fillBySettings(&e, settings); err != nil {
t.Fatal(err)
}
t.Log(e)
c := new(Customer)
if err := fillBySettings(c, settings); err != nil {
t.Fatal(err)
}
t.Log(*c)
}
type Customer struct {
CookieID string
Name string
Age int
}
/*
结构体填充
*/
func fillBySettings(st interface{}, settings map[string]interface{}) error {
// func (v Value) Elem() Value
// Elem returns the value that the interface v contains or that the pointer v points to.
// It panics if v's Kind is not Interface or Ptr.
// It returns the zero Value if v is nil.
if reflect.TypeOf(st).Kind() != reflect.Ptr {
return errors.New("the first param should be a pointer to the struct type.")
}
// Elem() 获取指针指向的值
if reflect.TypeOf(st).Elem().Kind() != reflect.Struct {
return errors.New("the first param should be a pointer to the struct type.")
}
if settings == nil {
return errors.New("settings is nil.")
}
var (
field reflect.StructField
ok bool
)
for k, v := range settings {
// 检查类型里有没有field (因为是指针类型,需要用Elem(),获取指针类型的)
if field, ok = (reflect.ValueOf(st)).Elem().Type().FieldByName(k); !ok {
continue
}
if field.Type == reflect.TypeOf(v) {
vstr := reflect.ValueOf(st)
vstr = vstr.Elem()
vstr.FieldByName(k).Set(reflect.ValueOf(v))
}
}
return nil
}
func TestFillNameAndAge(t *testing.T) {
settings := map[string]interface{}{"Name": "Mike", "Age": 30}
e := Employee{}
if err := fillBySettings(&e, settings); err != nil {
t.Fatal(err)
}
t.Log(e)
c := new(Customer)
if err := fillBySettings(c, settings); err != nil {
t.Fatal(err)
}
t.Log(*c)
}
反射 unsafe poiner
不安全编程的危险性
指针可以转换成任意类型的指针,利用它来实现类型转换. 非常危险
i := 10
f := *(float64)(unsafe.Pointer(&i))
func TestUnsafe(t *testing.T) {
i := 10
f := *(*float64)(unsafe.Pointer(&i))
t.Log(unsafe.Pointer(&i)) // 0xc000016298
t.Log(f) // 5e-323
}
i := 10
f := *(float64)(unsafe.Pointer(&i))
func TestUnsafe(t *testing.T) {
i := 10
f := *(*float64)(unsafe.Pointer(&i))
t.Log(unsafe.Pointer(&i)) // 0xc000016298
t.Log(f) // 5e-323
}
atomic 包 进行指针的读写操作
//原子类型操作
func TestAtomic(t *testing.T) {
var shareBufPtr unsafe.Pointer
writeDataFn := func() {
data := []int{}
for i := 0; i < 100; i++ {
data = append(data, i)
}
atomic.StorePointer(&shareBufPtr, unsafe.Pointer(&data))
}
readDataFn := func() {
data := atomic.LoadPointer(&shareBufPtr)
fmt.Println(data, *(*[]int)(data))
}
var wg sync.WaitGroup
writeDataFn()
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
for i := 0; i < 10; i++ {
writeDataFn()
time.Sleep(time.Microsecond * 100)
}
wg.Done()
}()
wg.Add(1)
go func() {
for i := 0; i < 10; i++ {
readDataFn()
time.Sleep(time.Microsecond * 100)
}
wg.Done()
}()
}
wg.Wait()
}
//原子类型操作
func TestAtomic(t *testing.T) {
var shareBufPtr unsafe.Pointer
writeDataFn := func() {
data := []int{}
for i := 0; i < 100; i++ {
data = append(data, i)
}
atomic.StorePointer(&shareBufPtr, unsafe.Pointer(&data))
}
readDataFn := func() {
data := atomic.LoadPointer(&shareBufPtr)
fmt.Println(data, *(*[]int)(data))
}
var wg sync.WaitGroup
writeDataFn()
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
for i := 0; i < 10; i++ {
writeDataFn()
time.Sleep(time.Microsecond * 100)
}
wg.Done()
}()
wg.Add(1)
go func() {
for i := 0; i < 10; i++ {
readDataFn()
time.Sleep(time.Microsecond * 100)
}
wg.Done()
}()
}
wg.Wait()
}
架构
Pip-Filter 架构
适合数据处理,数据分析
相当于一个个的 filter,对数据进行处理。
- 非常适合与数据处理及数据分析系统
- Filter 疯转数据处理的功能
- 松耦合:Filter 只和数据(格式)耦合
- Pipe 用于连接 Filter 传递数据或者在异步处理过程中缓冲数据流
- 进程内同步调用时,pipe 演变为数据在方法调用间传递
代码示例
源码文件见 quicktouch/go-learn
架构模式 micro kernel
特点:
- 易于扩展
- 错误隔离
- 保持架构一致性
要点
- 内核包含公共流程或通用逻辑
- 将可变或可扩展部门规划为扩展点
- 抽象扩展行为,定义接口
- 利用插件进行扩展
json 解析
内置的 json 解析
- 利用反射实现,通过 FieldTag 来标识对应的 json 值
因为利用了反射,性能不行,一般可用于配置文件的解析
type BasicInfo struct {
Name string `json:"name"`
Age int `json:"age"`
}
type JobInfo struct {
Skills []string `json:"skills"`
}
type EmployeeStruct struct {
BasicInfo BasicInfo `json:"basic_info"`
JobInfo JobInfo `json:"job_info"`
}
var jsonStr = `{
"basic_info": {
"name":"Mike",
"age":30
},
"job_info": {
"skills":["Java","Go","C"]
}
}`
func TestEmbeddedJson(t *testing.T) {
e := new(EmployeeStruct)
// json 赋值给struct
err := json.Unmarshal([]byte(jsonStr), e)
if err != nil {
t.Error(err) // {{Mike 30} {[Java Go C]}}
}
fmt.Println(*e)
// struct反序列化
if v, err := json.Marshal(e); err == nil {
fmt.Println(string(v))
// {"basic_info":{"name":"Mike","age":30},"job_info":{"skills":["Java","Go","C"]}}
} else {
t.Error(err)
}
}
type BasicInfo struct {
Name string `json:"name"`
Age int `json:"age"`
}
type JobInfo struct {
Skills []string `json:"skills"`
}
type EmployeeStruct struct {
BasicInfo BasicInfo `json:"basic_info"`
JobInfo JobInfo `json:"job_info"`
}
var jsonStr = `{
"basic_info": {
"name":"Mike",
"age":30
},
"job_info": {
"skills":["Java","Go","C"]
}
}`
func TestEmbeddedJson(t *testing.T) {
e := new(EmployeeStruct)
// json 赋值给struct
err := json.Unmarshal([]byte(jsonStr), e)
if err != nil {
t.Error(err) // {{Mike 30} {[Java Go C]}}
}
fmt.Println(*e)
// struct反序列化
if v, err := json.Marshal(e); err == nil {
fmt.Println(string(v))
// {"basic_info":{"name":"Mike","age":30},"job_info":{"skills":["Java","Go","C"]}}
} else {
t.Error(err)
}
}
EasyJson
采用代码生成而非反射.
安装: go get -u github.com/mailru/easyjson/...
使用 EasyJson 生成对应的解析方法文件(struct 文件需要在 gopath 下):
~/go/bin/easyjson -all /path/to/fileName.go
var jsonStr = `{
"basic_info": {
"name":"Mike",
"age":30
},
"job_info": {
"skills":["Java","Go","C"]
}
}`
func TestEasyJson(t *testing.T) {
e := EmployeeStruct{}
err := e.UnmarshalJSON([]byte(jsonStr))
if err != nil {
t.Error(err)
} else {
fmt.Println(e) // {{Mike 30} {[Java Go C]}}
}
if v, err := e.MarshalJSON(); err != nil {
t.Error(err)
} else {
fmt.Println(string(v)) // {"basic_info":{"name":"Mike","age":30},"job_info":{"skills":["Java","Go","C"]}}
}
}
var jsonStr = `{
"basic_info": {
"name":"Mike",
"age":30
},
"job_info": {
"skills":["Java","Go","C"]
}
}`
func TestEasyJson(t *testing.T) {
e := EmployeeStruct{}
err := e.UnmarshalJSON([]byte(jsonStr))
if err != nil {
t.Error(err)
} else {
fmt.Println(e) // {{Mike 30} {[Java Go C]}}
}
if v, err := e.MarshalJSON(); err != nil {
t.Error(err)
} else {
fmt.Println(string(v)) // {"basic_info":{"name":"Mike","age":30},"job_info":{"skills":["Java","Go","C"]}}
}
}
性能测试
使用go test -bench=.
进行性能测试
func BenchmarkEasyJson(b *testing.B) {
b.ResetTimer()
e := EmployeeStruct{}
for i := 0; i < b.N; i++ {
err := e.UnmarshalJSON([]byte(jsonStr))
if err != nil {
b.Error(err)
}
if _, err := e.MarshalJSON(); err != nil {
b.Error(err)
}
}
}
func BenchmarkEmbedJson(b *testing.B) {
b.ResetTimer()
e := new(EmployeeStruct)
for i := 0; i < b.N; i++ {
// json 赋值给struct
err := json.Unmarshal([]byte(jsonStr), e)
if err != nil {
b.Error(err)
}
if _, err := json.Marshal(e); err != nil {
b.Error(err)
}
}
}
// go test -bench=.
//goos: darwin
//goarch: amd64
//BenchmarkEasyJson-8 1262138 (运行次数) 936 ns/op (每次运行的时间/纳秒)
//BenchmarkEmbedJson-8 334260 3494 ns/op
func BenchmarkEasyJson(b *testing.B) {
b.ResetTimer()
e := EmployeeStruct{}
for i := 0; i < b.N; i++ {
err := e.UnmarshalJSON([]byte(jsonStr))
if err != nil {
b.Error(err)
}
if _, err := e.MarshalJSON(); err != nil {
b.Error(err)
}
}
}
func BenchmarkEmbedJson(b *testing.B) {
b.ResetTimer()
e := new(EmployeeStruct)
for i := 0; i < b.N; i++ {
// json 赋值给struct
err := json.Unmarshal([]byte(jsonStr), e)
if err != nil {
b.Error(err)
}
if _, err := json.Marshal(e); err != nil {
b.Error(err)
}
}
}
// go test -bench=.
//goos: darwin
//goarch: amd64
//BenchmarkEasyJson-8 1262138 (运行次数) 936 ns/op (每次运行的时间/纳秒)
//BenchmarkEmbedJson-8 334260 3494 ns/op
http 示例
自带的 http
url 匹配:
- URL 分为两种,末尾是
/:
表示一个子树,后面可以跟其他子路径;末尾不是/
,表示一个叶子,固定的路径。 - 以
/
结尾的 url 可以匹配它的任何子路径,比如/images
会匹配到/images/1.jpg
- 采用最长匹配原则,如果有多个匹配,一定采用匹配路径最长的那个进行处理
- 如果没有找到任何匹配项,会返回 404 错误
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello world") // 返回给客户端
})
// 可以匹配 time/*http.HandleFunc("/time/", func(w http.ResponseWriter, r *http.Request) {
t := time.Now()
timeStr := fmt.Sprintf("{\"time\":\"%s\"}", t)
_, _ = w.Write([]byte(timeStr))
})
_ = http.ListenAndServe(":8080", nil)
}
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello world") // 返回给客户端
})
// 可以匹配 time/*http.HandleFunc("/time/", func(w http.ResponseWriter, r *http.Request) {
t := time.Now()
timeStr := fmt.Sprintf("{\"time\":\"%s\"}", t)
_, _ = w.Write([]byte(timeStr))
})
_ = http.ListenAndServe(":8080", nil)
}
第三方框架
https://github.com/julienschmidt/httprouter
更简单,也方便构建 RESTful
go get -u github.com/julienschmidt/httprouter
示例:
func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Fprint(w, "welcome!")
}
func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
fmt.Fprintf(w, "hello, %s", ps.ByName("name"))
}
func main() {
router := httprouter.New()
router.GET("/", Index)
router.GET("/hello/:name", Hello)
log.Fatal(http.ListenAndServe(":8080", router))
}
// http://localhost:8080/hello/google -> hello, google
func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Fprint(w, "welcome!")
}
func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
fmt.Fprintf(w, "hello, %s", ps.ByName("name"))
}
func main() {
router := httprouter.New()
router.GET("/", Index)
router.GET("/hello/:name", Hello)
log.Fatal(http.ListenAndServe(":8080", router))
}
// http://localhost:8080/hello/google -> hello, google
性能分析 工具
相关依赖安装
安装 graphviz 添加图形的支持
brew install graphviz
安装 go-torch
- go get github.com/uber/go-torch
- git clone https://github.com/brendangregg/FlameGraph.git 将 flamegraph.pl 拷贝到
/usr/local/bin
目录下 - 输入
flamegraph.pl -h
可以看到是否成功
通过文件方式输出 Profile
- 灵活性高,适用于特定代码段的分析
- 通过手动调用 runtime/pprof 的 api
- api 相关文档 https://studygolang.com/static/pkgoc/pkg/runtime_pprof.htm
- go tool pprof [binary] [binary.prof]
可以输出 cpu 内存 线程数等。
相关文档查看:
- go help testflag
- https://golang.org/pkg/runtime/pprof/
编译测试代码:
# 编译测试代码
go build prof.go
# 运行二进制, 运行完多了 cpu.prof goroutine.prof mem.prof 这几个文件
./prof
# 编译测试代码
go build prof.go
# 运行二进制, 运行完多了 cpu.prof goroutine.prof mem.prof 这几个文件
./prof
查看这些文件 (适合短时间运行的程序)
go tool pprof 编译出的二进制 cpu.prof/goroutine.prof/mem.prof
例如:
go tool pprof prof cpu.prof
# log 如下,
# 随后使用top命令可以看到cpu详情。 flat表示所占时间和所占比例。 cum/cum%表示这个函数还调用了别的函数,总体加和在一起所占的时间和比例。
# 使用list + 符号名 命令可看具体哪行的耗时
# svg 以图像的方式查看调用关系及具体的cpu耗时,比较直观。
# exit 退出
File: profType: cpuTime: Feb 2, 2020 at 10:44pm (CST)
Duration: 2.06s, Total samples = 1.81s (87.91%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 1.81s, 100% of 1.81s totalShowing top 10 nodes out of 12flat flat% sum% cum cum%1.77s 97.79% 97.79% 1.78s 98.34% main.fillMatrix0.02s 1.10% 98.90% 0.02s 1.10% main.calculate0.01s 0.55% 99.45% 0.01s 0.55% math/rand.(*rngSource).Int63
0.01s 0.55% 100% 0.01s 0.55% runtime.newstack0 0% 100% 1.81s 100% main.main0 0% 100% 0.01s 0.55% math/rand.(*Rand).Int31
0 0% 100% 0.01s 0.55% math/rand.(*Rand).Int31n
0 0% 100% 0.01s 0.55% math/rand.(*Rand).Int63
0 0% 100% 0.01s 0.55% math/rand.(*Rand).Intn
0 0% 100% 0.01s 0.55% os.Create(pprof) list fillMatrixTotal: 1.81sROUTINE ======================== main.fillMatrix in /Users/panda/Desktop/go-learn/playground/src/lession/tools/file/prof.go1.77s 1.78s (flat, cum) 98.34% of Total
. . 15:
. . 16:func fillMatrix(m *[row][col]int) {
. . 17: s := rand.New(rand.NewSource(time.Now().UnixNano()))
. . 18: for i := 0; i < row; i++ {
. . 19: for j := 0; j < col; j++ {
1.77s 1.78s 20: m[i][j] = s.Intn(100000)
. . 21: }
. . 22: }
. . 23:}
. . 24:
. . 25:func calculate(m *[row][col]int) {
(pprof) exit
go tool pprof prof cpu.prof
# log 如下,
# 随后使用top命令可以看到cpu详情。 flat表示所占时间和所占比例。 cum/cum%表示这个函数还调用了别的函数,总体加和在一起所占的时间和比例。
# 使用list + 符号名 命令可看具体哪行的耗时
# svg 以图像的方式查看调用关系及具体的cpu耗时,比较直观。
# exit 退出
File: profType: cpuTime: Feb 2, 2020 at 10:44pm (CST)
Duration: 2.06s, Total samples = 1.81s (87.91%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 1.81s, 100% of 1.81s totalShowing top 10 nodes out of 12flat flat% sum% cum cum%1.77s 97.79% 97.79% 1.78s 98.34% main.fillMatrix0.02s 1.10% 98.90% 0.02s 1.10% main.calculate0.01s 0.55% 99.45% 0.01s 0.55% math/rand.(*rngSource).Int63
0.01s 0.55% 100% 0.01s 0.55% runtime.newstack0 0% 100% 1.81s 100% main.main0 0% 100% 0.01s 0.55% math/rand.(*Rand).Int31
0 0% 100% 0.01s 0.55% math/rand.(*Rand).Int31n
0 0% 100% 0.01s 0.55% math/rand.(*Rand).Int63
0 0% 100% 0.01s 0.55% math/rand.(*Rand).Intn
0 0% 100% 0.01s 0.55% os.Create(pprof) list fillMatrixTotal: 1.81sROUTINE ======================== main.fillMatrix in /Users/panda/Desktop/go-learn/playground/src/lession/tools/file/prof.go1.77s 1.78s (flat, cum) 98.34% of Total
. . 15:
. . 16:func fillMatrix(m *[row][col]int) {
. . 17: s := rand.New(rand.NewSource(time.Now().UnixNano()))
. . 18: for i := 0; i < row; i++ {
. . 19: for j := 0; j < col; j++ {
1.77s 1.78s 20: m[i][j] = s.Intn(100000)
. . 21: }
. . 22: }
. . 23:}
. . 24:
. . 25:func calculate(m *[row][col]int) {
(pprof) exit
go-torch
go-torch 查看火炬图。
go-torch cpu.prof```
### 以 http 的方式输出 profile
- 简单,适合持续性运行的程序
- 应用程序中导入`import _ "net/http/pprof"`, 并启动 http server 即可
- `http://<host>:<port>/debug/pprof/`
- `go tool pprof http://<host>:<port>/debug/pprof/profile?seconds-10` (默认 30s)
- `go-torch -seconds 10 http://<host>:<port>/debug/pprof/profile`
```go
import (
"fmt"
"net/http"
_ "net/http/pprof"
)
func GetFibonacciSeries(n int) ([]int, error) {
fibList := []int{1, 1}
for i := 2; i < n; i++ {
fibList = append(fibList, fibList[i-2]+fibList[i-1])
}
return fibList, nil
}
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("welcome!"))
})
http.HandleFunc("/fb", func(w http.ResponseWriter, r *http.Request) {
var fabs []int
for i := 0; i < 100000; i++ {
fabs, _ = GetFibonacciSeries(50)
}
w.Write([]byte(fmt.Sprintf("%v", fabs)))
})
_ = http.ListenAndServe(":8080", nil)
}
/*
访问 http://localhost:8080/debug/pprof/ 可看到一些分析
点击profile会进行30秒的采样,并下载文件。
也能用命令行
go tool pprof http://<host>:<port>/debug/pprof/profile?seconds=10
然后使用 top命令 (top排序,如按cum排序: top -cum)
命令: list 方法名
等。
*/
go-torch cpu.prof```
### 以 http 的方式输出 profile
- 简单,适合持续性运行的程序
- 应用程序中导入`import _ "net/http/pprof"`, 并启动 http server 即可
- `http://<host>:<port>/debug/pprof/`
- `go tool pprof http://<host>:<port>/debug/pprof/profile?seconds-10` (默认 30s)
- `go-torch -seconds 10 http://<host>:<port>/debug/pprof/profile`
```go
import (
"fmt"
"net/http"
_ "net/http/pprof"
)
func GetFibonacciSeries(n int) ([]int, error) {
fibList := []int{1, 1}
for i := 2; i < n; i++ {
fibList = append(fibList, fibList[i-2]+fibList[i-1])
}
return fibList, nil
}
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("welcome!"))
})
http.HandleFunc("/fb", func(w http.ResponseWriter, r *http.Request) {
var fabs []int
for i := 0; i < 100000; i++ {
fabs, _ = GetFibonacciSeries(50)
}
w.Write([]byte(fmt.Sprintf("%v", fabs)))
})
_ = http.ListenAndServe(":8080", nil)
}
/*
访问 http://localhost:8080/debug/pprof/ 可看到一些分析
点击profile会进行30秒的采样,并下载文件。
也能用命令行
go tool pprof http://<host>:<port>/debug/pprof/profile?seconds=10
然后使用 top命令 (top排序,如按cum排序: top -cum)
命令: list 方法名
等。
*/