Golang单元测试、最大子数组、效能分析

一、目的

  • Go语言中的单元测试库介绍
  • 性能测试题目:数组中最大的子数组之和
  • Go语言效能分析

二、编程

1. Golang单元测试库介绍

Golang内置了单元测试的库testing,利用该库可以进行单元测试。
对于一个可被testing库测试的源文件有两点要求:

  1. 文件名以test结尾,比如hello_test.go
  2. 单元测试的函数名字要以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在程序执行过程中的信息:
image
也可以以生成函数调用图,如下:

go tool pprof -svg ./cpu.pprof

image

posted on 2021-03-12 23:27  FreestyleCoding  阅读(159)  评论(1编辑  收藏  举报

导航