2014-06-20 35 views
0

我有下面的代碼,它使用的內存,這是遠遠高於預期的色調。
我以前pprof工具,它示出了功能NewEdge被分配由程序分配的所有存儲器的94%以上。神祕而過多的內存分配函數中的圍棋程序

我的問題是,什麼是錯的這個代碼,這是使用這麼多的內存:

type Vertex struct { 
    Id      string    `json:"id"`   // must be unique 
    Properties    map[string]string `json:"properties"` // to be implemented soon 
    verticesThisIsConnectedTo map[string][]string `json:"-"`   //id for the edges *Edge // keys are Vertex ids, each pair of vertices can be connected to each other with multiple edges 
    verticesConnectedToThis map[string][]string `json:"_"`   //id for the edges *Edge // keys are Vertex ids, 
} 
type Edge struct { 
    id   string   `json:"-"` // for internal use, unique 
    Label  string   `json:"label"` 
    SourceId string   `json:"source-id"` 
    TargetId string   `json:"terget-id"` 
    Type  string   `json:"type"` 
    Properties map[string]string `json:"properties"` // to be implemented soon 
} 
func (v *Vertex) isPartof(g *Graph) bool { 
    _, b := g.Vertices[v.Id] 
    return b 
} 
func (g *Graph) NewEdge(source, target *Vertex, label, edgeType string) (Edge, error) { 
    if source.Id == target.Id { 
     return Edge{}, ERROR_NO_EDGE_TO_SELF_ALLOWED 
    } 
    if !source.isPartof(g) || !target.isPartof(g) { 
     return Edge{}, errors.New("InvalidEdge, source or target not in this graph") 
    } 

    e := Edge{id: <-nextId, Label: label, SourceId: source.Id, TargetId: target.Id, Type: edgeType} 
    g.Edges[e.id] = &e 

    source.verticesThisIsConnectedTo[target.Id] = append(source.verticesThisIsConnectedTo[target.Id], e.id) 
    target.verticesConnectedToThis[source.Id] = append(target.verticesConnectedToThis[source.Id], e.id) 
    return e, nil 
} 

分配情況通過這樣一個電話:fakeGraph(Aragog, 2000, 1)其中:

func fakeGraph(g Graph, nodesCount, followratio int) error { 
    var err error 
    // create the vertices 
    for i := 0; i < nodesCount; i++ { 
      v := NewVertex("") //FH.RandStr(10)) 
      g.AddVertex(v) 
    } 
    // create some "follow edges" 
    followcount := followratio * nodesCount/100 
    vkeys := []string{} 
    for pk := range g.Vertices { 
      vkeys = append(vkeys, pk) 
    } 
    for ki := range g.Vertices { 
      pidx := rand.Perm(nodesCount) 
      followcounter := followcount 
      for j := 0; j < followcounter; j++ { 
        _, err := g.NewEdge(g.Vertices[ki], g.Vertices[vkeys[pidx[j]]], <-nextId, EDGE_TYPE_FOLLOW) 
        if err != nil { 
          followcounter++ // to compensate for references to self 
        } 
      } 
    } 
    return err 
    } 

問題/神祕

我可以創建成千上萬的Vertex s和內存使用是非常reaso nable。但撥打NewEdge是非常內存密集。我首先注意到代碼使用了內存的語調。我跑pprof-memprofile,然後用go tool pprof並得到了這一點:

(pprof) top10 
Total: 9.9 MB 
    8.9 89.9% 89.9%  8.9 89.9% main.(*Graph).NewEdge 
    0.5 5.0% 95.0%  0.5 5.0% allocg 
    0.5 5.0% 100.0%  0.5 5.0% fmt.Sprintf 
    0.0 0.0% 100.0%  0.5 5.0% _rt0_go 
    0.0 0.0% 100.0%  8.9 89.9% main.fakeGraph 
    0.0 0.0% 100.0%  0.5 5.0% main.func·003 
    0.0 0.0% 100.0%  8.9 89.9% main.main 
    0.0 0.0% 100.0%  0.5 5.0% mcommoninit 
(pprof) 

任何幫助是非常讚賞。

+0

你沒告訴你是如何分配的頂點。也許開銷實際上是邊緣id分配給源和目標ID地圖?你爲這10MB分配了多少個對象? –

+0

@not_a_Golfer謝謝,我編輯了這個問題,現在它包括我如何創建它們。 – Ali

+0

我沒有特別看到問題。這取決於您創建的Edge數量以及各種Ids的大小。 – mzimmerman

回答

1

@ali我認爲在這個內存分析沒有什麼神祕的。首先,如果檢查結構的大小,您將看到Edge結構比Vertex結構大2倍。 (您可以通過unsafe.Sizeof()檢查結構的大小) 所以,如果你將調用fakeGraph(阿拉戈克,2000年,1)圍棋將分配:

  • 2000頂點結構
  • 至少2000 * 20 = 40層000邊緣結構
    正如你可以看到新際()將分配至少40倍以上的內存,則fakeGraph()

而且,每次你將嘗試創建新的邊緣時,新的邊緣結構將分配 - 即使NewEdge()返回錯誤。

的另一個因素是 - 你返回結構本身,而不是指向struct。在Go結構中是值類型,所以一旦你從NewEdge()返回,整個結構將被複制,並且它也會導致新的分配。 是的,我明白你從不使用返回結構,但我不知道如果去編譯器會檢查調用者的上下文,並跳過邊複印

+0

Thanks @varyous我不知道它是否解釋了所有問題,但是我忽略了基本數學(邊數)的事實,是我嚇壞的主要原因。我仍然需要深入一點,但這解釋了很多問題,並且爲了記錄,我在幾個小時前就想出了它,當時我試圖向朋友解釋問題,並開始計算對象。但是,仍然感謝您抽出時間閱讀代碼。我很感激。 – Ali