Comparison of b against GoLLRB (as at d5f020). $ benchcmp gollrb.bench b-gen.bench benchmark old ns/op new ns/op delta BenchmarkNamePredicate 1631932 1409531 -13.63% BenchmarkLargeSetsNoIntersection 190792654 63748682 -66.59% BenchmarkVeryLargeSetsSmallIntersection 896154437 373475843 -58.32% BenchmarkHelplessContainsChecker 20719182678 14078301640 -32.05% BenchmarkNetAndSpeed 32519019 20188665 -37.92% BenchmarkKeanuAndNet 18319247 15224988 -16.89% BenchmarkKeanuAndSpeed 30849568 18744134 -39.24% BenchmarkKeanuOther 105552525 107620648 +1.96% BenchmarkKeanuBullockOther 295395338 115193002 -61.00% benchmark old allocs new allocs delta BenchmarkNamePredicate 1339 1341 +0.15% BenchmarkLargeSetsNoIntersection 22585 23632 +4.64% BenchmarkVeryLargeSetsSmallIntersection 65776 69396 +5.50% BenchmarkHelplessContainsChecker 1713541 2036316 +18.84% BenchmarkNetAndSpeed 17104 17240 +0.80% BenchmarkKeanuAndNet 15816 15855 +0.25% BenchmarkKeanuAndSpeed 16368 16493 +0.76% BenchmarkKeanuOther 30134 30634 +1.66% BenchmarkKeanuBullockOther 35510 36454 +2.66% benchmark old bytes new bytes delta BenchmarkNamePredicate 96162 96294 +0.14% BenchmarkLargeSetsNoIntersection 1172356 1249872 +6.61% BenchmarkVeryLargeSetsSmallIntersection 2810080 2992409 +6.49% BenchmarkHelplessContainsChecker 89233264 104999088 +17.67% BenchmarkNetAndSpeed 1388793 1428110 +2.83% BenchmarkKeanuAndNet 1263145 1250079 -1.03% BenchmarkKeanuAndSpeed 1246956 1281546 +2.77% BenchmarkKeanuOther 2021312 2024727 +0.17% BenchmarkKeanuBullockOther 2671448 2742968 +2.68% Conflicts: graph/memstore/triplestore.go
250 lines
5.7 KiB
Go
250 lines
5.7 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 memstore
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/barakmich/glog"
|
|
|
|
"github.com/google/cayley/graph"
|
|
"github.com/google/cayley/graph/iterator"
|
|
"github.com/google/cayley/graph/memstore/b"
|
|
"github.com/google/cayley/quad"
|
|
)
|
|
|
|
func init() {
|
|
graph.RegisterTripleStore("memstore", false, func(string, graph.Options) (graph.TripleStore, error) {
|
|
return newTripleStore(), nil
|
|
}, nil)
|
|
}
|
|
|
|
type QuadDirectionIndex struct {
|
|
index [4]map[int64]*b.Tree
|
|
}
|
|
|
|
func NewQuadDirectionIndex() QuadDirectionIndex {
|
|
return QuadDirectionIndex{[...]map[int64]*b.Tree{
|
|
quad.Subject - 1: make(map[int64]*b.Tree),
|
|
quad.Predicate - 1: make(map[int64]*b.Tree),
|
|
quad.Object - 1: make(map[int64]*b.Tree),
|
|
quad.Label - 1: make(map[int64]*b.Tree),
|
|
}}
|
|
}
|
|
|
|
func (qdi QuadDirectionIndex) Tree(d quad.Direction, id int64) *b.Tree {
|
|
if d < quad.Subject || d > quad.Label {
|
|
panic("illegal direction")
|
|
}
|
|
tree, ok := qdi.index[d-1][id]
|
|
if !ok {
|
|
tree = b.TreeNew(cmp)
|
|
qdi.index[d-1][id] = tree
|
|
}
|
|
return tree
|
|
}
|
|
|
|
func (qdi QuadDirectionIndex) Get(d quad.Direction, id int64) (*b.Tree, bool) {
|
|
if d < quad.Subject || d > quad.Label {
|
|
panic("illegal direction")
|
|
}
|
|
tree, ok := qdi.index[d-1][id]
|
|
return tree, ok
|
|
}
|
|
|
|
type LogEntry struct {
|
|
graph.Delta
|
|
DeletedBy int64
|
|
}
|
|
|
|
type TripleStore struct {
|
|
idCounter int64
|
|
quadIdCounter int64
|
|
idMap map[string]int64
|
|
revIdMap map[int64]string
|
|
log []LogEntry
|
|
size int64
|
|
index QuadDirectionIndex
|
|
// vip_index map[string]map[int64]map[string]map[int64]*b.Tree
|
|
}
|
|
|
|
func newTripleStore() *TripleStore {
|
|
return &TripleStore{
|
|
idMap: make(map[string]int64),
|
|
revIdMap: make(map[int64]string),
|
|
|
|
// Sentinel null entry so indices start at 1
|
|
log: make([]LogEntry, 1, 200),
|
|
|
|
index: NewQuadDirectionIndex(),
|
|
idCounter: 1,
|
|
quadIdCounter: 1,
|
|
}
|
|
}
|
|
|
|
func (ts *TripleStore) ApplyDeltas(deltas []graph.Delta) error {
|
|
for _, d := range deltas {
|
|
var err error
|
|
if d.Action == graph.Add {
|
|
err = ts.AddDelta(d)
|
|
} else {
|
|
err = ts.RemoveDelta(d)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
const maxInt = int(^uint(0) >> 1)
|
|
|
|
func (ts *TripleStore) indexOf(t quad.Quad) (int64, bool) {
|
|
min := maxInt
|
|
var tree *b.Tree
|
|
for d := quad.Subject; d <= quad.Label; d++ {
|
|
sid := t.Get(d)
|
|
if d == quad.Label && sid == "" {
|
|
continue
|
|
}
|
|
id, ok := ts.idMap[sid]
|
|
// If we've never heard about a node, it must not exist
|
|
if !ok {
|
|
return 0, false
|
|
}
|
|
index, ok := ts.index.Get(d, id)
|
|
if !ok {
|
|
// If it's never been indexed in this direction, it can't exist.
|
|
return 0, false
|
|
}
|
|
if l := index.Len(); l < min {
|
|
min, tree = l, index
|
|
}
|
|
}
|
|
it := NewIterator(tree, "", ts)
|
|
|
|
for it.Next() {
|
|
val := it.Result()
|
|
if t == ts.log[val.(int64)].Quad {
|
|
return val.(int64), true
|
|
}
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
func (ts *TripleStore) AddDelta(d graph.Delta) error {
|
|
if _, exists := ts.indexOf(d.Quad); exists {
|
|
return graph.ErrQuadExists
|
|
}
|
|
qid := ts.quadIdCounter
|
|
ts.log = append(ts.log, LogEntry{Delta: d})
|
|
ts.size++
|
|
ts.quadIdCounter++
|
|
|
|
for dir := quad.Subject; dir <= quad.Label; dir++ {
|
|
sid := d.Quad.Get(dir)
|
|
if dir == quad.Label && sid == "" {
|
|
continue
|
|
}
|
|
if _, ok := ts.idMap[sid]; !ok {
|
|
ts.idMap[sid] = ts.idCounter
|
|
ts.revIdMap[ts.idCounter] = sid
|
|
ts.idCounter++
|
|
}
|
|
}
|
|
|
|
for dir := quad.Subject; dir <= quad.Label; dir++ {
|
|
if dir == quad.Label && d.Quad.Get(dir) == "" {
|
|
continue
|
|
}
|
|
id := ts.idMap[d.Quad.Get(dir)]
|
|
tree := ts.index.Tree(dir, id)
|
|
tree.Set(qid, struct{}{})
|
|
}
|
|
|
|
// TODO(barakmich): Add VIP indexing
|
|
return nil
|
|
}
|
|
|
|
func (ts *TripleStore) RemoveDelta(d graph.Delta) error {
|
|
prevQuadID, exists := ts.indexOf(d.Quad)
|
|
if !exists {
|
|
return graph.ErrQuadNotExist
|
|
}
|
|
|
|
quadID := ts.quadIdCounter
|
|
ts.log = append(ts.log, LogEntry{Delta: d})
|
|
ts.log[prevQuadID].DeletedBy = quadID
|
|
ts.size--
|
|
ts.quadIdCounter++
|
|
return nil
|
|
}
|
|
|
|
func (ts *TripleStore) Quad(index graph.Value) quad.Quad {
|
|
return ts.log[index.(int64)].Quad
|
|
}
|
|
|
|
func (ts *TripleStore) TripleIterator(d quad.Direction, value graph.Value) graph.Iterator {
|
|
index, ok := ts.index.Get(d, value.(int64))
|
|
data := fmt.Sprintf("dir:%s val:%d", d, value.(int64))
|
|
if ok {
|
|
return NewIterator(index, data, ts)
|
|
}
|
|
return &iterator.Null{}
|
|
}
|
|
|
|
func (ts *TripleStore) Horizon() int64 {
|
|
return ts.log[len(ts.log)-1].ID
|
|
}
|
|
|
|
func (ts *TripleStore) Size() int64 {
|
|
return ts.size
|
|
}
|
|
|
|
func (ts *TripleStore) DebugPrint() {
|
|
for i, l := range ts.log {
|
|
if i == 0 {
|
|
continue
|
|
}
|
|
glog.V(2).Infof("%d: %#v", i, l)
|
|
}
|
|
}
|
|
|
|
func (ts *TripleStore) ValueOf(name string) graph.Value {
|
|
return ts.idMap[name]
|
|
}
|
|
|
|
func (ts *TripleStore) NameOf(id graph.Value) string {
|
|
return ts.revIdMap[id.(int64)]
|
|
}
|
|
|
|
func (ts *TripleStore) TriplesAllIterator() graph.Iterator {
|
|
return NewMemstoreQuadsAllIterator(ts)
|
|
}
|
|
|
|
func (ts *TripleStore) FixedIterator() graph.FixedIterator {
|
|
return iterator.NewFixedIteratorWithCompare(iterator.BasicEquality)
|
|
}
|
|
|
|
func (ts *TripleStore) TripleDirection(val graph.Value, d quad.Direction) graph.Value {
|
|
name := ts.Quad(val).Get(d)
|
|
return ts.ValueOf(name)
|
|
}
|
|
|
|
func (ts *TripleStore) NodesAllIterator() graph.Iterator {
|
|
return NewMemstoreNodesAllIterator(ts)
|
|
}
|
|
|
|
func (ts *TripleStore) Close() {}
|