diff --git a/query/benchmark/README.txt b/query/benchmark/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..64f152a96a29e7cdb737597aaca8c85ca1d0dabb --- /dev/null +++ b/query/benchmark/README.txt @@ -0,0 +1,36 @@ +The files in this folder contain gobencoded data for a processed SubGraph for +the following queries. The number at the end(10,100,1000) of the files +represents the number of entities in the results of the query. + +Actors query +{ + me(_xid_:m.08624h) { + type.object.name.en + film.actor.film { + film.performance.film { + type.object.name.en + } + } + } +} + +Directors query +{ + me(_xid_:m.05dxl_) { + type.object.name.en + film.director.film { + film.film.genre { + type.object.name.en + } + } + } +} + +Benchmarking tests were run for ToJson and ToProtocolBuffer methods. Results +from the `go test` command are tabulated below. + +BenchmarkToJson 500 3939003 ns/op 957723 B/op 16115 allocs/op +BenchmarkToProtocolBuffer 1000 2288681 ns/op 566287 B/op 7542 allocs/op + +We can see that ToProtocolBuffer method allocates less memory and takes lesser +time than ToJson method. \ No newline at end of file diff --git a/query/benchmark/actors10.txt b/query/benchmark/actors10.txt new file mode 100644 index 0000000000000000000000000000000000000000..7c8e357b4a004f2935c9d18f4df01445020f646a Binary files /dev/null and b/query/benchmark/actors10.txt differ diff --git a/query/benchmark/actors100.txt b/query/benchmark/actors100.txt new file mode 100644 index 0000000000000000000000000000000000000000..1913d51d53728b2b3a25f0c1c2033c0309f2a95c Binary files /dev/null and b/query/benchmark/actors100.txt differ diff --git a/query/benchmark/actors1000.txt b/query/benchmark/actors1000.txt new file mode 100644 index 0000000000000000000000000000000000000000..e68e12919c6211372b8ad5345ee05ba81f1b99b4 Binary files /dev/null and b/query/benchmark/actors1000.txt differ diff --git a/query/benchmark/directors10.txt b/query/benchmark/directors10.txt new file mode 100644 index 0000000000000000000000000000000000000000..30626b0ddb0bc0f008d89eace50f25f51aa58dc5 Binary files /dev/null and b/query/benchmark/directors10.txt differ diff --git a/query/benchmark/directors100.txt b/query/benchmark/directors100.txt new file mode 100644 index 0000000000000000000000000000000000000000..f490b15013d0b8a59179c33591a9fcb6a0de6485 Binary files /dev/null and b/query/benchmark/directors100.txt differ diff --git a/query/benchmark/directors1000.txt b/query/benchmark/directors1000.txt new file mode 100644 index 0000000000000000000000000000000000000000..8e362902146e92ac589233ea614fbb4c1d019a51 Binary files /dev/null and b/query/benchmark/directors1000.txt differ diff --git a/query/query.go b/query/query.go index 19ea07f8f40aef96f6f21d80af8a561d24ddaac9..956fc9cbdddc2d049af8e1e6c48d6305bd016e75 100644 --- a/query/query.go +++ b/query/query.go @@ -115,8 +115,8 @@ type SubGraph struct { Offset int Children []*SubGraph - query []byte - result []byte + Query []byte + Result []byte } func mergeInterfaces(i1 interface{}, i2 interface{}) interface{} { @@ -138,7 +138,7 @@ func mergeInterfaces(i1 interface{}, i2 interface{}) interface{} { } func postTraverse(g *SubGraph) (result map[uint64]interface{}, rerr error) { - if len(g.query) == 0 { + if len(g.Query) == 0 { return result, nil } @@ -163,13 +163,13 @@ func postTraverse(g *SubGraph) (result map[uint64]interface{}, rerr error) { } // Now read the query and results at current node. - uo := flatbuffers.GetUOffsetT(g.query) + uo := flatbuffers.GetUOffsetT(g.Query) q := new(task.Query) - q.Init(g.query, uo) + q.Init(g.Query, uo) - ro := flatbuffers.GetUOffsetT(g.result) + ro := flatbuffers.GetUOffsetT(g.Result) r := new(task.Result) - r.Init(g.result, ro) + r.Init(g.Result, ro) if q.UidsLength() != r.UidmatrixLength() { glog.Fatalf("Result uidmatrixlength: %v. Query uidslength: %v", @@ -277,18 +277,19 @@ func indexOf(uid uint64, q *task.Query) int { // This method gets the values and children for a subgraph. func (g *SubGraph) preTraverse(uid uint64, dst *graph.Node) error { - properties := make(map[string]*graph.Value) + var properties map[string]*graph.Value + properties = make(map[string]*graph.Value) var children []*graph.Node // We go through all predicate children of the subgraph. for _, pc := range g.Children { - ro := flatbuffers.GetUOffsetT(pc.result) + ro := flatbuffers.GetUOffsetT(pc.Result) r := new(task.Result) - r.Init(pc.result, ro) + r.Init(pc.Result, ro) - uo := flatbuffers.GetUOffsetT(pc.query) + uo := flatbuffers.GetUOffsetT(pc.Query) q := new(task.Query) - q.Init(pc.query, uo) + q.Init(pc.Query, uo) idx := indexOf(uid, q) @@ -354,13 +355,13 @@ func (g *SubGraph) preTraverse(uid uint64, dst *graph.Node) error { func (g *SubGraph) ToProtocolBuffer(l *Latency) (n *graph.Node, rerr error) { n = &graph.Node{} n.Attribute = g.Attr - if len(g.query) == 0 { + if len(g.Query) == 0 { return n, nil } - ro := flatbuffers.GetUOffsetT(g.result) + ro := flatbuffers.GetUOffsetT(g.Result) r := new(task.Result) - r.Init(g.result, ro) + r.Init(g.Result, ro) var ul task.UidList r.Uidmatrix(&ul, 0) @@ -448,9 +449,9 @@ func newGraph(euid uint64, exid string) (*SubGraph, error) { sg := new(SubGraph) sg.Attr = "_root_" - sg.result = b.Bytes[b.Head():] + sg.Result = b.Bytes[b.Head():] // Also add query for consistency and to allow for ToJson() later. - sg.query = createTaskQuery(sg, []uint64{euid}) + sg.Query = createTaskQuery(sg, []uint64{euid}) return sg, nil } @@ -535,8 +536,8 @@ func ProcessGraph(sg *SubGraph, rch chan error, td time.Duration) { timeout := time.Now().Add(td) var err error - if len(sg.query) > 0 && sg.Attr != "_root_" { - sg.result, err = worker.ProcessTaskOverNetwork(sg.query) + if len(sg.Query) > 0 && sg.Attr != "_root_" { + sg.Result, err = worker.ProcessTaskOverNetwork(sg.Query) if err != nil { x.Err(glog, err).Error("While processing task.") rch <- err @@ -544,9 +545,9 @@ func ProcessGraph(sg *SubGraph, rch chan error, td time.Duration) { } } - uo := flatbuffers.GetUOffsetT(sg.result) + uo := flatbuffers.GetUOffsetT(sg.Result) r := new(task.Result) - r.Init(sg.result, uo) + r.Init(sg.Result, uo) if r.ValuesLength() > 0 { var v task.Value @@ -589,7 +590,7 @@ func ProcessGraph(sg *SubGraph, rch chan error, td time.Duration) { childchan := make(chan error, len(sg.Children)) for i := 0; i < len(sg.Children); i++ { child := sg.Children[i] - child.query = createTaskQuery(child, sorted) + child.Query = createTaskQuery(child, sorted) go ProcessGraph(child, childchan, timeleft) } diff --git a/query/query_test.go b/query/query_test.go index 01a676da31e89d3e0774ec0de3cc6c5062872a7a..5bcd9ad42f88ee95a99e1fbcc060fc545c2ba179 100644 --- a/query/query_test.go +++ b/query/query_test.go @@ -17,6 +17,8 @@ package query import ( + "bytes" + "encoding/gob" "fmt" "io/ioutil" "os" @@ -63,12 +65,12 @@ func checkName(t *testing.T, r *task.Result, idx int, expected string) { func checkSingleValue(t *testing.T, child *SubGraph, attr string, value string) { - if child.Attr != attr || len(child.result) == 0 { - t.Error("Expected attr name with some result") + if child.Attr != attr || len(child.Result) == 0 { + t.Error("Expected attr name with some.Result") } - uo := flatbuffers.GetUOffsetT(child.result) + uo := flatbuffers.GetUOffsetT(child.Result) r := new(task.Result) - r.Init(child.result, uo) + r.Init(child.Result, uo) if r.ValuesLength() != 1 { t.Errorf("Expected value length 1. Got: %v", r.ValuesLength()) } @@ -105,9 +107,9 @@ func TestNewGraph(t *testing.T) { worker.Init(ps, nil, 0, 1) - uo := flatbuffers.GetUOffsetT(sg.result) + uo := flatbuffers.GetUOffsetT(sg.Result) r := new(task.Result) - r.Init(sg.result, uo) + r.Init(sg.Result, uo) if r.UidmatrixLength() != 1 { t.Errorf("Expected length 1. Got: %v", r.UidmatrixLength()) } @@ -230,13 +232,13 @@ func TestProcessGraph(t *testing.T) { if child.Attr != "friend" { t.Errorf("Expected attr friend. Got: %v", child.Attr) } - if len(child.result) == 0 { - t.Errorf("Expected some result.") + if len(child.Result) == 0 { + t.Errorf("Expected some.Result.") return } - uo := flatbuffers.GetUOffsetT(child.result) + uo := flatbuffers.GetUOffsetT(child.Result) r := new(task.Result) - r.Init(child.result, uo) + r.Init(child.Result, uo) if r.UidmatrixLength() != 1 { t.Errorf("Expected 1 matrix. Got: %v", r.UidmatrixLength()) @@ -257,8 +259,8 @@ func TestProcessGraph(t *testing.T) { t.Errorf("Expected attr name") } child = child.Children[0] - uo = flatbuffers.GetUOffsetT(child.result) - r.Init(child.result, uo) + uo = flatbuffers.GetUOffsetT(child.Result) + r.Init(child.Result, uo) if r.ValuesLength() != 5 { t.Errorf("Expected 5 names of 5 friends") } @@ -420,3 +422,71 @@ func TestToProtocolBuffer(t *testing.T) { t.Errorf("Expected 0 children, Got: %v", len(child.Children)) } } + +func benchmarkToJson(file string, b *testing.B) { + b.ReportAllocs() + var sg SubGraph + var l Latency + + f, err := ioutil.ReadFile(file) + if err != nil { + b.Error(err) + } + + buf := bytes.NewBuffer(f) + dec := gob.NewDecoder(buf) + err = dec.Decode(&sg) + if err != nil { + b.Error(err) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err := sg.ToJson(&l); err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkToJson(b *testing.B) { + benchmarkToJson("benchmark/actors10.txt", b) + benchmarkToJson("benchmark/actors100.txt", b) + benchmarkToJson("benchmark/actors1000.txt", b) + benchmarkToJson("benchmark/directors10.txt", b) + benchmarkToJson("benchmark/directors100.txt", b) + benchmarkToJson("benchmark/directors1000.txt", b) +} + +func benchmarkToProtocolBuffer(file string, b *testing.B) { + b.ReportAllocs() + var sg SubGraph + var l Latency + + f, err := ioutil.ReadFile(file) + if err != nil { + b.Error(err) + } + + buf := bytes.NewBuffer(f) + dec := gob.NewDecoder(buf) + err = dec.Decode(&sg) + if err != nil { + b.Error(err) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err := sg.ToProtocolBuffer(&l); err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkToProtocolBuffer(b *testing.B) { + benchmarkToProtocolBuffer("benchmark/actors10.txt", b) + benchmarkToProtocolBuffer("benchmark/actors100.txt", b) + benchmarkToProtocolBuffer("benchmark/actors1000.txt", b) + benchmarkToProtocolBuffer("benchmark/directors10.txt", b) + benchmarkToProtocolBuffer("benchmark/directors100.txt", b) + benchmarkToProtocolBuffer("benchmark/directors1000.txt", b) +}