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

时间:2014-11-07 19:39:19

标签: memory time go profiling

我最近在大型数据集上运行了一些用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

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

为什么这三个号码的排列更紧密?

1 个答案:

答案 0 :(得分: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)
    }
}

输出:

$ 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以KBytes而不是页面报告。我的Ubuntu版本已修补。

参考文献:

Re: GNU time: incorrect results

time-1.7 counts rusage wrong on Linux

GNU Project Archives: time

“time” 1.7-24 source package in Ubuntu。以KBytes而不是页面报告ru_maxrss。 (关闭:#649402)

#649402 - [PATCH] time overestimates max RSS by a factor of 4 - Debian Bug report logs

  

主题:修复ru_maxrss报告作者:Richard Kettlewell   Bug-Debian:http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=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);