我最近在大型数据集上运行了一些用Go编写的数字代码,并且遇到了内存管理问题.在尝试分析问题时,我用三种不同的方式测量了程序的内存使用情况:使用Go的runtime/pprof
包,使用unix time
实用程序,以及手动累加我分配的数据大小.这三种方法并没有给我一致的结果.
下面是我正在分析的代码的简化版本.它分配了几个切片,将值放在每个索引处,并将每个切片放在父切片中:
package main import ( "fmt" "os" "runtime/pprof" "unsafe" "flag" ) var mprof = flag.String("mprof", "", "write memory profile to this file") func main() { flag.Parse() N := 1<<15 psSlice := make([][]int64, N) _ = psSlice size := 0 for i := 0; i < N; i++ { ps := make([]int64, 1<<10) for i := range ps { ps[i] = int64(i) } psSlice[i] = ps size += int(unsafe.Sizeof(ps[0])) * len(ps) } if *mprof != "" { f, err := os.Create(*mprof) if err != nil { panic(err) } pprof.WriteHeapProfile(f) f.Close() } fmt.Printf("total allocated: %d MB\n", size >> 20) }
使用该命令运行此$ time time -f "%M kB" ./mem_test -mprof=out.mprof
结果将导致输出:
total allocated: 256 MB 1141216 kB real 0m0.150s user 0m0.031s sys 0m0.113s
这里第一个数字,256 MB,只是从中计算的数组的大小,unsafe.Sizeof
第二个数字,1055 MB,是time
报告的数字.运行pprof工具会导致
(pprof) top1 Total: 108.2 MB 107.8 99.5% 99.5% 107.8 99.5% main.main
对于较小或较大长度的切片,这些结果可以按照您期望的方式平滑地缩放.
为什么这三个数字不紧密排列?
首先,您需要提供一个无错误的示例.让我们从基本数字开始.例如,
package main import ( "fmt" "runtime" "unsafe" ) func WriteMatrix(nm [][]int64) { for n := range nm { for m := range nm[n] { nm[n][m]++ } } } func NewMatrix(n, m int) [][]int64 { a := make([]int64, n*m) nm := make([][]int64, n) lo, hi := 0, m for i := range nm { nm[i] = a[lo:hi:hi] lo, hi = hi, hi+m } return nm } func MatrixSize(nm [][]int64) int64 { size := int64(0) for i := range nm { size += int64(unsafe.Sizeof(nm[i])) for j := range nm[i] { size += int64(unsafe.Sizeof(nm[i][j])) } } return size } var nm [][]int64 func main() { n, m := 1<<15, 1<<10 var ms1, ms2 runtime.MemStats runtime.ReadMemStats(&ms1) nm = NewMatrix(n, m) WriteMatrix(nm) runtime.ReadMemStats(&ms2) fmt.Println(runtime.GOARCH, runtime.GOOS) fmt.Println("Actual: ", ms2.TotalAlloc-ms1.TotalAlloc) fmt.Println("Estimate:", n*3*8+n*m*8) fmt.Println("Total: ", ms2.TotalAlloc) fmt.Println("Size: ", MatrixSize(nm)) // check top VIRT and RES for COMMAND peter for { WriteMatrix(nm) } }
输出:
$ go build peter.go && /usr/bin/time -f "%M KiB" ./peter amd64 linux Actual: 269221888 Estimate: 269221888 Total: 269240592 Size: 269221888 ^C Command exited with non-zero status 2 265220 KiB $ $ top VIRT 284268 RES 265136 COMMAND peter
这是你所期望的吗?
请参阅MatrixSize
计算内存大小的正确方法.
在允许我们使用top
命令的无限循环中,通过更新矩阵将矩阵固定为常驻.
运行此程序时会得到什么结果?
BUG:
你的结果/usr/bin/time
是 1056992 KiB
太大了四倍.这是在你的版本中的错误/usr/bin/time
,ru_maxrss
在千字节的页面没有报道.我的Ubuntu版本已修补.
参考文献:
Re:GNU时间:结果不正确
时间1.7计算Linux上的rusage错误
GNU项目档案:时间
Ubuntu中的"时间"1.7-24源代码包.ru_maxrss
报告以KB为单位而不是页面.(关闭:#649402)
#PATY]时间过高估计最多RSS 4倍 - Debian Bug报告日志
主题:修复ru_maxrss报告作者:Richard Kettlewell Bug-Debian:http://bugs.debian.org/cgi-bin/bugreport.cgi?video = 649402
--- time-1.7.orig/time.c +++ time-1.7/time.c @@ -392,7 +398,7 @@ ptok ((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS (v)); break; case 'M': /* Maximum resident set size. */ - fprintf (fp, "%lu", ptok ((UL) resp->ru.ru_maxrss)); + fprintf (fp, "%lu", (UL) resp->ru.ru_maxrss); break; case 'O': /* Outputs. */ fprintf (fp, "%ld", resp->ru.ru_oublock);