graph: add edgeIndex's to Graph to speed up EdgeValues call

This commit is contained in:
Brian Picciano 2018-08-17 11:08:26 -06:00
parent 132a50039b
commit 91c0629377

View File

@ -43,6 +43,46 @@ func (e Edge) id() string {
return fmt.Sprintf("%q-%q->%q", e.Tail, e.Val, e.Head) return fmt.Sprintf("%q-%q->%q", e.Tail, e.Val, e.Head)
} }
// an edgeIndex maps valueIDs to a set of edgeIDs. Graph keeps two edgeIndex's,
// one for input edges and one for output edges.
type edgeIndex map[string]map[string]struct{}
func (ei edgeIndex) cp() edgeIndex {
if ei == nil {
return edgeIndex{}
}
ei2 := make(edgeIndex, len(ei))
for valID, edgesM := range ei {
edgesM2 := make(map[string]struct{}, len(edgesM))
for id := range edgesM {
edgesM2[id] = struct{}{}
}
ei2[valID] = edgesM2
}
return ei2
}
func (ei edgeIndex) add(valID, edgeID string) {
edgesM, ok := ei[valID]
if !ok {
edgesM = map[string]struct{}{}
ei[valID] = edgesM
}
edgesM[edgeID] = struct{}{}
}
func (ei edgeIndex) del(valID, edgeID string) {
edgesM, ok := ei[valID]
if !ok {
return
}
delete(edgesM, edgeID)
if len(edgesM) == 0 {
delete(ei, valID)
}
}
// Graph implements an immutable, unidirectional graph which can hold generic // Graph implements an immutable, unidirectional graph which can hold generic
// values. All methods are thread-safe as they don't modify the Graph in any // values. All methods are thread-safe as they don't modify the Graph in any
// way. // way.
@ -53,11 +93,17 @@ func (e Edge) id() string {
// Edges are in random order. // Edges are in random order.
type Graph struct { type Graph struct {
m map[string]Edge m map[string]Edge
// these are indices mapping Value IDs to all the in/out edges for that
// Value in the Graph.
vIns, vOuts edgeIndex
} }
func (g Graph) cp() Graph { func (g Graph) cp() Graph {
g2 := Graph{ g2 := Graph{
m: make(map[string]Edge, len(g.m)), m: make(map[string]Edge, len(g.m)),
vIns: g.vIns.cp(),
vOuts: g.vOuts.cp(),
} }
for id, e := range g.m { for id, e := range g.m {
g2.m[id] = e g2.m[id] = e
@ -75,6 +121,8 @@ func (g Graph) AddEdge(e Edge) Graph {
g2 := g.cp() g2 := g.cp()
g2.m[id] = e g2.m[id] = e
g2.vIns.add(e.Head.ID, id)
g2.vOuts.add(e.Tail.ID, id)
return g2 return g2
} }
@ -88,6 +136,8 @@ func (g Graph) DelEdge(e Edge) Graph {
g2 := g.cp() g2 := g.cp()
delete(g2.m, id) delete(g2.m, id)
g2.vIns.del(e.Head.ID, id)
g2.vOuts.del(e.Tail.ID, id)
return g2 return g2
} }
@ -121,14 +171,14 @@ func (g Graph) Edges() []Edge {
// ValueEdges returns all input (e.Head==v) and output (e.Tail==v) Edges // ValueEdges returns all input (e.Head==v) and output (e.Tail==v) Edges
// for the given Value in the Graph. // for the given Value in the Graph.
func (g Graph) ValueEdges(v Value) ([]Edge, []Edge) { func (g Graph) ValueEdges(v Value) ([]Edge, []Edge) {
var in, out []Edge in := make([]Edge, 0, len(g.vIns[v.ID]))
for _, e := range g.m { for edgeID := range g.vIns[v.ID] {
if e.Tail.ID == v.ID { in = append(in, g.m[edgeID])
out = append(out, e)
}
if e.Head.ID == v.ID {
in = append(in, e)
} }
out := make([]Edge, 0, len(g.vOuts[v.ID]))
for edgeID := range g.vOuts[v.ID] {
out = append(out, g.m[edgeID])
} }
return in, out return in, out
} }