Golang Alloc и HeapAlloc против значительных расхождений - PullRequest
3 голосов
/ 15 мая 2019

У меня есть программа Go, которая вычисляет большие корреляционные матрицы в памяти.Для этого я настроил конвейер из 3 процедур, где первый читает файлы, второй вычисляет матрицу корреляции, а последний сохраняет результат на диск.

Проблема в том, что когда я запускаю программу, среда выполнения Go выделяет ~ 17 ГБ памяти, а матрица занимает всего ~ 2-3 ГБ.Использование runtime.ReadMemStats показывает, что программа использует ~ 17 ГБ (и проверено с помощью htop), но pprof сообщает только о ~ 2,3 ГБ.

Если я посмотрю статистику mem после запуска одного файла черезконвейер:

var mem runtime.MemStats
runtime.ReadMemStats(&mem)
fmt.Printf("Total alloc: %d GB\n", mem.Alloc/1000/1000/1000)

Это показывает общее выделение программы:

Total alloc: 17 GB

Однако, если я запускаю go tool pprof mem.prof, я получаю следующие результаты:

(pprof) top5
Showing nodes accounting for 2.21GB, 100% of 2.21GB total
Showing top 5 nodes out of 9
      flat  flat%   sum%        cum   cum%
    1.20GB 54.07% 54.07%     1.20GB 54.07%  dataset.(*Dataset).CalcCorrelationMatrix
    1.02GB 45.93%   100%     1.02GB 45.93%  bytes.makeSlice
         0     0%   100%     1.02GB 45.93%  bytes.(*Buffer).WriteByte
         0     0%   100%     1.02GB 45.93%  bytes.(*Buffer).grow
         0     0%   100%     1.02GB 45.93%  encoding/json.Indent

Так что мне интересно, как мне узнать, почему программа выделяет 17 ГБ, когда кажется, что пиковое использование памяти составляет всего ~ 2,5 ГБ?Есть ли способ отследить использование памяти во всей программе, используя pprof?

EDIT

Я снова запустил программу с GODEBUG = gctrace = 1 и получил следующую трассировку:

gc 1 @0.017s 0%: 0.005+0.55+0.003 ms clock, 0.022+0/0.47/0.11+0.012 ms cpu, 1227->1227->1226 MB, 1228 MB goal, 4 P
gc 2 @14.849s 0%: 0.003+1.7+0.004 ms clock, 0.015+0/1.6/0.11+0.018 ms cpu, 1227->1227->1227 MB, 2452 MB goal, 4 P
gc 3 @16.850s 0%: 0.006+60+0.003 ms clock, 0.027+0/0.46/59+0.015 ms cpu, 1876->1876->1712 MB, 2455 MB goal, 4 P
gc 4 @22.861s 0%: 0.005+238+0.003 ms clock, 0.021+0/0.46/237+0.015 ms cpu, 3657->3657->3171 MB, 3658 MB goal, 4 P
gc 5 @30.716s 0%: 0.005+476+0.004 ms clock, 0.022+0/0.44/476+0.017 ms cpu, 5764->5764->5116 MB, 6342 MB goal, 4 P
gc 6 @46.023s 0%: 0.005+949+0.004 ms clock, 0.020+0/0.47/949+0.017 ms cpu, 10302->10302->9005 MB, 10303 MB goal, 4 P
gc 7 @64.878s 0%: 0.006+382+0.004 ms clock, 0.024+0/0.46/382+0.019 ms cpu, 16548->16548->7728 MB, 18011 MB goal, 4 P
gc 8 @89.774s 0%: 0.86+2805+0.006 ms clock, 3.4+0/24/2784+0.025 ms cpu, 20208->20208->17088 MB, 20209 MB goal, 4 P

Таким образом, совершенно очевидно, что куча постоянно растет через программу, но я не могу точно определить, где.Я профилировал использование памяти с помощью pprof.WriteHeapProfile после вызова функций, интенсивно использующих память:

func memoryProfile(profpath string) {

    if _, err := os.Stat(profpath); os.IsNotExist(err) {
        os.Mkdir(profpath, os.ModePerm)
    }

    f, err := os.Create(path.Join(profpath, "mem.mprof"))
    fmt.Printf("Creating memory profile in %s", "data/profile/mem.mprof\n")
    if err != nil {
        panic(err)
    }

    if err := pprof.WriteHeapProfile(f); err != nil {
        panic(err)
    }
    f.Close()
}
...