我有以下代码,它使用内存的音调,这比预期的要高。
我曾经使用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
并且内存使用非常合理。但是对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 :(得分:1)
@ali我认为这种记忆分析没有神秘感。 首先,如果检查结构的大小,您将看到Edge结构比Vertex结构大2倍。 (你可以通过unsafe.Sizeof()来检查结构的大小) 所以,如果你打电话给fakeGraph(Aragog,2000,1),Go会分配:
此外,每次尝试创建新边缘时,都会分配新的Edge结构 - 即使NewEdge()返回错误。
另一个因素是 - 你返回struct本身,而不是指向struct的指针。在Go中,struct是值类型,因此一旦从NewEdge()返回整个结构将被复制,它也会导致新的分配。 是的,我看到你从未使用过返回的结构,但我不确定Go编译器是否会检查调用者的上下文并跳过边缘复制