Golang单元测试、最大子数组、效能分析
一、目的
- Go语言中的单元测试库介绍
- 性能测试题目:数组中最大的子数组之和
- Go语言效能分析
二、编程
1. Golang单元测试库介绍
Golang内置了单元测试的库testing
,利用该库可以进行单元测试。
对于一个可被testing库测试的源文件有两点要求:
- 文件名以
test
结尾,比如hello_test.go
- 单元测试的函数名字要以
Test
开头,函数参数必须是t *testing.T
, 比如func TestHello(t *testing.T){}
,
如下是要被测试的代码:
// hello_test.go
package hello_test
import "testing"
func TestHello(t *testing.T) {
t.Log("hello")
}
然后输入
go test 包名 -v 函数名
,
比如:
go test hello_test -v TestHello
就会执行想要测试的函数,对于上述的hello_test.go
,在控制台会输出下面结果:
图中画红线的部分就是代码中输出语句的结果。
2. 求数组中最大的子数组之和
题目:
求数组中的子数组中和最大的子数组。
比如数组[-3, -4, 5, -3, -1]
最大子数组是[5]
。
比如数组[-3, 5, -1, 6, -4]
最大子数组是[5, -1, 6]
.
分析:
对于这道题,求子数组,那么就是原数组中连续的若干个元素组成的数组,第一反应想到双指针解法,虽然不是最优的。
对于双指针解法,代码如下:
// max_subarray_test.go
package max_subarray_test
import "testing"
// 数组求和函数
func sum(nums []int) int {
sum := 0
for _, num := range nums {
sum += num
}
return sum
}
// 求一个数组的最大子数组
// 本方案时间复杂度O(N^2)
func maxSubArr(arr []int) (int, []int) {
inputArr := arr
maxSubArr := inputArr[:]
leftIndex := 0
rightIndex := len(inputArr) - 1
max := inputArr[0]
// 双指针法,从两侧向中间逼近
for leftIndex <= rightIndex {
// 如果在某个范围,子数组的和比最大值大,那么保存当前子数组和最大值
temp := sum(inputArr[leftIndex : rightIndex+1])
if temp > max {
max = temp
maxSubArr = inputArr[leftIndex : rightIndex+1]
}
// 如果该子数组只剩下一个元素了,直接返回
if len(maxSubArr) <= 1 {
break
}
// 继续向中间逼近,看左边和右边的数哪个数小,小的向中间走
if inputArr[leftIndex] < inputArr[rightIndex] {
leftIndex++
} else {
rightIndex--
}
}
return max, maxSubArr
}
func TestMaxSubArray(t *testing.T) {
inputArr := []int{-2, -3, -5, -1, -9}
max, subArr := maxSubArr(inputArr)
t.Logf("输入的数组:%v,最大子数组:%v,最大值:%v", inputArr, max, subArr)
inputArr = []int{-2, 4, -3, 6, -9}
max, subArr = maxSubArr(inputArr)
t.Logf("输入的数组:%v,最大子数组:%v,最大值:%v", inputArr, max, subArr)
}
输入命令:
go test max_subarray_test.go -v TestMaxSubArray
显示如下结果:
3.Golang效能分析工具
Golang自带了性能分析工具pprof
,可以记录在程序运行过程中CPU和内存的使用情况。
这里就测试上述的“求数组中最大子数组”的代码,测试如下:
// max_subarray.go
package main
import (
"flag"
"fmt"
"log"
"os"
"runtime"
"runtime/pprof"
)
// 数组求和函数
func sum(nums []int) int {
sum := 0
for _, num := range nums {
sum += num
}
return sum
}
// 求一个数组的最大子数组
// 本方案时间复杂度O(N^2)
func maxSubArr(arr []int) (int, []int) {
inputArr := arr
maxSubArr := inputArr[:]
leftIndex := 0
rightIndex := len(inputArr) - 1
max := inputArr[0]
// 双指针法,从两侧向中间逼近
for leftIndex <= rightIndex {
// 如果在某个范围,子数组的和比最大值大,那么保存当前子数组和最大值
temp := sum(inputArr[leftIndex : rightIndex+1])
if temp > max {
max = temp
maxSubArr = inputArr[leftIndex : rightIndex+1]
}
// 如果该子数组只剩下一个元素了,直接返回
if len(maxSubArr) <= 1 {
break
}
// 继续向中间逼近,看左边和右边的数哪个数小,小的向中间走
if inputArr[leftIndex] < inputArr[rightIndex] {
leftIndex++
} else {
rightIndex--
}
}
return max, maxSubArr
}
func contentTesting() {
for i := 0; i < 10000; i++ {
inputArr := []int{-2, -3, -5, -1, -9}
max, subArr := maxSubArr(inputArr)
fmt.Printf("输入的数组:%v,最大子数组:%v,最大值:%v\n", inputArr, max, subArr)
}
}
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
var memprofile = flag.String("memprofile", "", "write memory profile to `file`")
func main() {
flag.Parse()
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal("could not create CPU profile: ", err)
}
defer f.Close() // error handling omitted for example
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("could not start CPU profile: ", err)
}
defer pprof.StopCPUProfile()
contentTesting()
}
contentTesting()
if *memprofile != "" {
f, err := os.Create(*memprofile)
if err != nil {
log.Fatal("could not create memory profile: ", err)
}
defer f.Close() // error handling omitted for example
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
log.Fatal("could not write memory profile: ", err)
}
contentTesting()
}
}
然后编译上述源文件:
go build max_subarray.go
会生成可执行文件max_subarray
,然后执行该可执行文件,输入参数,表示输出cpu信息:
./max_subarray --cpuprofile=cpu.pprof
此时会生成名称为cpu.pprof
的二进制文件,无法直接打开,需要使用golang的too工具下的pprof打开它:
go tool pprof cpu.pprof
可以看到CPU在程序执行过程中的信息:
也可以以生成函数调用图,如下:
go tool pprof -svg ./cpu.pprof
posted on 2021-03-12 23:27 FreestyleCoding 阅读(159) 评论(1) 编辑 收藏 举报