Memory 在Go中评测内存时,结果似乎不一致

Memory 在Go中评测内存时,结果似乎不一致,memory,time,go,profiling,Memory,Time,Go,Profiling,我最近一直在运行一些用Go-on大型数据集编写的数字代码,并且遇到了内存管理问题。在试图分析问题的同时,我用三种不同的方法测量了我的程序的内存使用情况:使用Go的runtime/pprof包,使用unixtime实用程序,以及通过手动添加我分配的数据大小。这三种方法的结果并不一致 下面是我正在分析的代码的简化版本。它分配多个切片,在每个索引处放置值,并将每个值放置在父切片内: package main import ( "fmt" "os" "runtime/ppro

我最近一直在运行一些用Go-on大型数据集编写的数字代码,并且遇到了内存管理问题。在试图分析问题的同时,我用三种不同的方法测量了我的程序的内存使用情况:使用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)
}
这里,第一个数字256MB是根据
unsafe.Sizeof
计算的数组大小,第二个数字1055MB是
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)
    }
}

只需在我的机器上运行您的代码,并获得“总分配:1024MB”(OS X 10.9.5)以及您正在使用的go版本,您正在使用的目标体系结构是什么?我的示例代码中存在错误,我现在已经更正了这些错误。我正在运行适用于linux/AMD64的Go 1.3.3,我已经纠正了示例代码中的错误。当我运行你的代码时,我得到的结果与你的一致。然而,我仍然从时间实用程序中得到了四个因子。下面是我运行代码时发生的情况:$go build peter.go&/usr/bin/time-f“%M KiB”。/peter amd64 linux实际值:269256384估计值:269221888总计:26935416大小:269221888 1056992 KiB,仍然显示了四个因子的差异。我决定调查我的
时间是否正常工作。我在主函数末尾为{}
放置了一个
,在后台运行,然后调用
top
top
表示进程使用了278 MB内存。这似乎表明
time
工作不正常(不知何故)。@mansfield:要使
top
驻留值(
RES
)测量矩阵的内存,请通过在无限循环中写入使其驻留。看我修改过的计划,这是一个很好的观点。但是,我得到了更新程序的
VIRT 278m
RES 258m
,以及
time
报告的
1057008 KiB
。您的答案是正确的。我已经联系了集群工作人员,了解了您提供给我的信息,他们确认这就是发生的事情。谢谢你花时间帮我找到这个。
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
--- 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);