236 lines
6.3 KiB
Go
236 lines
6.3 KiB
Go
// Copyright 2014 The Cayley Authors. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package main
|
|
|
|
import (
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/google/cayley/config"
|
|
"github.com/google/cayley/db"
|
|
"github.com/google/cayley/graph"
|
|
"github.com/google/cayley/query/gremlin"
|
|
)
|
|
|
|
var benchmarkQueries = []struct {
|
|
message string
|
|
query string
|
|
tag string
|
|
expect []string
|
|
}{
|
|
// Easy one to get us started. How quick is the most straightforward retrieval?
|
|
{
|
|
message: "name predicate",
|
|
query: `
|
|
g.V("Humphrey Bogart").In("name").All()
|
|
`,
|
|
expect: []string{":/en/humphrey_bogart"},
|
|
},
|
|
|
|
// Grunty queries.
|
|
// 2014-07-12: This one seems to return in ~20ms in memory;
|
|
// that's going to be measurably slower for every other backend.
|
|
{
|
|
message: "two large sets with no intersection",
|
|
query: `
|
|
function getId(x) { return g.V(x).In("name") }
|
|
var actor_to_film = g.M().In("/film/performance/actor").In("/film/film/starring")
|
|
|
|
getId("Oliver Hardy").Follow(actor_to_film).Out("name").Intersect(
|
|
getId("Mel Blanc").Follow(actor_to_film).Out("name")).All()
|
|
`,
|
|
expect: []string{},
|
|
},
|
|
|
|
// 2014-07-12: This one takes about 4 whole seconds in memory. This is a behemoth.
|
|
{
|
|
message: "three huge sets with small intersection",
|
|
query: `
|
|
function getId(x) { return g.V(x).In("name") }
|
|
var actor_to_film = g.M().In("/film/performance/actor").In("/film/film/starring")
|
|
|
|
var a = getId("Oliver Hardy").Follow(actor_to_film).FollowR(actor_to_film)
|
|
var b = getId("Mel Blanc").Follow(actor_to_film).FollowR(actor_to_film)
|
|
var c = getId("Billy Gilbert").Follow(actor_to_film).FollowR(actor_to_film)
|
|
|
|
seen = {}
|
|
|
|
a.Intersect(b).Intersect(c).ForEach(function (d) {
|
|
if (!(d.id in seen)) {
|
|
seen[d.id] = true;
|
|
g.Emit(d.id)
|
|
}
|
|
})
|
|
`,
|
|
expect: []string{":/en/billy_gilbert", ":/en/sterling_holloway"},
|
|
},
|
|
|
|
// This is more of an optimization problem that will get better over time. This takes a lot
|
|
// of wrong turns on the walk down to what is ultimately the name, but top AND has it easy
|
|
// as it has a fixed ID. Exercises Check().
|
|
{
|
|
message: "the helpless checker",
|
|
query: `
|
|
g.V().As("person").In("name").In().In().Out("name").Is("Casablanca").All()
|
|
`,
|
|
tag: "person",
|
|
expect: []string{
|
|
"Claude Rains",
|
|
"Conrad Veidt",
|
|
"Dooley Wilson",
|
|
"Helmut Dantine",
|
|
"Humphrey Bogart",
|
|
"Ingrid Bergman",
|
|
"John Qualen",
|
|
"Joy Page",
|
|
"Leonid Kinskey",
|
|
"Lou Marcelle",
|
|
"Madeleine LeBeau",
|
|
"Paul Henreid",
|
|
"Peter Lorre",
|
|
"Sydney Greenstreet",
|
|
"S.Z. Sakall",
|
|
},
|
|
},
|
|
|
|
//Q: Who starred in both "The Net" and "Speed" ?
|
|
//A: "Sandra Bullock"
|
|
{
|
|
message: "Net and Speed",
|
|
query: common + `m1_actors.Intersect(m2_actors).Out("name").All()
|
|
`,
|
|
},
|
|
|
|
//Q: Did "Keanu Reeves" star in "The Net" ?
|
|
//A: No
|
|
{
|
|
message: "Keanu in The Net",
|
|
query: common + `actor2.Intersect(m1_actors).Out("name").All()
|
|
`,
|
|
},
|
|
|
|
//Q: Did "Keanu Reeves" star in "Speed" ?
|
|
//A: Yes
|
|
{
|
|
message: "Keanu in Speed",
|
|
query: common + `actor2.Intersect(m2_actors).Out("name").All()
|
|
`,
|
|
},
|
|
|
|
//Q: Has "Keanu Reeves" co-starred with anyone who starred in "The Net" ?
|
|
//A: "Keanu Reeves" was in "Speed" and "The Lake House" with "Sandra Bullock",
|
|
// who was in "The Net"
|
|
{
|
|
message: "Keanu with other in The Net",
|
|
query: common + `actor2.Follow(coStars1).Intersect(m1_actors).Out("name").All()
|
|
`,
|
|
},
|
|
|
|
//Q5: Do "Keanu Reeves" and "Sandra Bullock" have any commons co-stars?
|
|
//A5: Yes, many. For example: SB starred with "Steve Martin" in "The Prince
|
|
// of Egypt", and KR starred with Steven Martin in "Parenthood".
|
|
{
|
|
message: "Keanu and Bullock with other",
|
|
query: common + `actor1.Save("name","costar1_actor").Follow(coStars1).Intersect(actor2.Save("name","costar2_actor").Follow(coStars2)).Out("name").All()
|
|
`,
|
|
},
|
|
}
|
|
|
|
const common = `
|
|
var movie1 = g.V().Has("name", "The Net")
|
|
var movie2 = g.V().Has("name", "Speed")
|
|
var actor1 = g.V().Has("name", "Sandra Bullock")
|
|
var actor2 = g.V().Has("name", "Keanu Reeves")
|
|
|
|
// (film) -> starring -> (actor)
|
|
var filmToActor = g.Morphism().Out("/film/film/starring").Out("/film/performance/actor")
|
|
|
|
// (actor) -> starring -> [film -> starring -> (actor)]
|
|
var coStars1 = g.Morphism().In("/film/performance/actor").In("/film/film/starring").Save("name","costar1_movie").Follow(filmToActor)
|
|
var coStars2 = g.Morphism().In("/film/performance/actor").In("/film/film/starring").Save("name","costar2_movie").Follow(filmToActor)
|
|
|
|
// Stars for the movies "The Net" and "Speed"
|
|
var m1_actors = movie1.Save("name","movie1").Follow(filmToActor)
|
|
var m2_actors = movie2.Save("name","movie2").Follow(filmToActor)
|
|
`
|
|
|
|
var (
|
|
once sync.Once
|
|
cfg = &config.Config{
|
|
DatabasePath: "30kmoviedata.nt.gz",
|
|
DatabaseType: "memstore",
|
|
GremlinTimeout: 1,
|
|
}
|
|
|
|
ts graph.TripleStore
|
|
)
|
|
|
|
func runBench(n int, b *testing.B) {
|
|
var err error
|
|
once.Do(func() {
|
|
ts, err = db.Open(cfg)
|
|
if err != nil {
|
|
b.Fatalf("Failed to open %q: %v", cfg.DatabasePath, err)
|
|
}
|
|
})
|
|
ses := gremlin.NewSession(ts, cfg.GremlinTimeout, true)
|
|
_, err = ses.InputParses(benchmarkQueries[n].query)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse benchmark gremlin %s: %v", benchmarkQueries[n].message, err)
|
|
}
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
c := make(chan interface{}, 5)
|
|
go ses.ExecInput(benchmarkQueries[n].query, c, 100)
|
|
for _ = range c {
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkNamePredicate(b *testing.B) {
|
|
runBench(0, b)
|
|
}
|
|
|
|
func BenchmarkLargeSetsNoIntersection(b *testing.B) {
|
|
runBench(1, b)
|
|
}
|
|
|
|
func BenchmarkVeryLargeSetsSmallIntersection(b *testing.B) {
|
|
runBench(2, b)
|
|
}
|
|
|
|
func BenchmarkHelplessChecker(b *testing.B) {
|
|
runBench(3, b)
|
|
}
|
|
|
|
func BenchmarkNetAndSpeed(b *testing.B) {
|
|
runBench(4, b)
|
|
}
|
|
|
|
func BenchmarkKeannuAndNet(b *testing.B) {
|
|
runBench(5, b)
|
|
}
|
|
|
|
func BenchmarkKeannuAndSpeed(b *testing.B) {
|
|
runBench(6, b)
|
|
}
|
|
|
|
func BenchmarkKeannuOther(b *testing.B) {
|
|
runBench(7, b)
|
|
}
|
|
|
|
func BenchmarkKeannuBullockOther(b *testing.B) {
|
|
runBench(8, b)
|
|
}
|