在Go中分析内存时看似不一致的结果

man*_*eld 3 memory time profiling go

我最近在大型数据集上运行了一些用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)
}
Run Code Online (Sandbox Code Playgroud)

使用该命令运行此$ 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
Run Code Online (Sandbox Code Playgroud)

这里第一个数字,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
Run Code Online (Sandbox Code Playgroud)

对于较小或较大长度的切片,这些结果可以按照您期望的方式平滑地缩放.

为什么这三个数字不紧密排列?

pet*_*rSO 5

首先,您需要提供一个无错误的示例.让我们从基本数字开始.例如,

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

$ 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/time1056992 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);
Run Code Online (Sandbox Code Playgroud)