Files
zeekay f5aaeb712e Hanzo Graph — the engine, on zapdb, ZAP-native
Derived from Dgraph (github.com/dgraph-io/dgraph) at commit 64804bd2, Apache-2.0.
See NOTICE.

WHAT THIS IS. The graph engine as a Go library: posting lists, the DQL parser,
the type and tokenizer system, the index and schema layers, and the wire types.
39,170 lines of the upstream's 166,337.

THE STORE is github.com/luxfi/zapdb, a Badger fork that kept the package name,
the subpackages (options, pb, y) and the API — Open, OpenManaged, DB, Options —
so the 74 import sites are aliased rather than rewritten.

THE CODEC is ZAP. zapdb serializes with ZAP, not protobuf: its pb package builds
the ZAP types by default and the protobuf ones only under `-tags grpc`, a path
that does not compile (table/builder.go calls pb.Marshal, which that tag
removes). One call site in posting needed converting; every other type here is
Hanzo's own protobuf and is untouched.

NO TRANSPORT. An embedded engine dials nothing, so nothing here imports
google.golang.org/grpc. Removed: 20 transport functions in x, the generated
service stubs for Raft, Zero and Worker (1,657 lines), and the JWT helper —
identity belongs to IAM. The core called none of them.

NOT KEPT: worker, conn, raftwal, the cluster commands, the GraphQL layer and the
test harnesses. `query`, the DQL execution engine, went with them because it
depends on worker; parsing DQL is available here, executing it is not.

The history of the upstream project lives at github.com/dgraph-io/dgraph. This
repository begins at the derivation because three quarters of that history
describes code this fork does not carry.
2026-08-20 21:26:27 -07:00

193 lines
4.7 KiB
Go

// The MIT License (MIT)
// Copyright (c) 2015-2016 robskie <[email protected]>
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package main
import (
"compress/gzip"
"encoding/binary"
"fmt"
"io"
"os"
"sort"
"time"
"github.com/golang/glog"
"github.com/hanzoai/graph/codec"
"github.com/hanzoai/graph/protos/pb"
"github.com/hanzoai/graph/x"
)
// So, with SIMD instructions before, we were getting great performance, but at the cost of
// complexity and bugs.
// $ go build . && ./benchmark
// BenchmarkDeltaPack64 551 mis
// BenchmarkDeltaUnPack64 2330 mis
//
// With the new simple codec, the code is simple and easy to work with, but we're getting quite poor
// performance. So, sometime later, it would be good to come back in here, and try and improve the
// performance of decoding.
// $ go build . && ./benchmark
// BenchmarkDeltaPack64 30 mis
// BenchmarkDeltaUnPack64 116 mis
// TODO: Improve performance here, using SIMD instructions.
const (
// chunkByteSize is the number of bytes per chunk of data.
chunkByteSize = 262144
)
func read(filename string) []int {
f, err := os.Open(filename)
x.Panic(err)
defer func() {
if err := f.Close(); err != nil {
glog.Warningf("error while closing fd: %v", err)
}
}()
fgzip, err := gzip.NewReader(f)
x.Panic(err)
defer fgzip.Close()
buf := make([]byte, 4)
_, err = fgzip.Read(buf)
if err != nil && err != io.EOF {
x.Panic(err)
}
ndata := binary.LittleEndian.Uint32(buf)
data := make([]int, ndata)
for i := range data {
_, err = fgzip.Read(buf)
if err != nil && err != io.EOF {
x.Panic(err)
}
data[i] = int(binary.LittleEndian.Uint32(buf))
}
return data
}
type chunks struct {
intSize int
data [][]uint64
length int
}
func chunkify64(data []int) *chunks {
const chunkLen = chunkByteSize / 8
nchunks := len(data) / chunkLen
cdata := make([][]uint64, nchunks)
n := 0
for i := range cdata {
chunk := make([]uint64, chunkLen)
for j := range chunk {
chunk[j] = uint64(data[n])
n++
}
cdata[i] = chunk
}
return &chunks{64, cdata, n}
}
func benchmarkPack(trials int, chunks *chunks) int {
times := make([]int, trials)
for i := range times {
start := time.Now()
for _, c := range chunks.data {
pack := codec.Encode(c, 256)
codec.FreePack(pack)
// bp128.DeltaPack(c)
}
times[i] = int(time.Since(start).Nanoseconds())
}
sort.Ints(times)
tmedian := times[len(times)/2]
speed := (float64(chunks.length) / float64(tmedian)) * 1e3
return int(speed)
}
func benchmarkUnpack(trials int, chunks *chunks) int {
packed := make([]*pb.UidPack, len(chunks.data))
for i, c := range chunks.data {
packed[i] = codec.Encode(c, 256)
}
times := make([]int, trials)
for i := range times {
start := time.Now()
for _, p := range packed {
dec := codec.Decoder{Pack: p}
for uids := dec.Seek(0, 0); len(uids) > 0; uids = dec.Next() {
}
}
times[i] = int(time.Since(start).Nanoseconds())
}
// Check if both input and output are equal
for i, c := range chunks.data {
out := codec.Decode(packed[i], 0)
for j := range c {
if c[j] != out[j] {
x.Fatalf("Something wrong %+v \n%+v\n %+v\n", len(c), len(out), j)
}
}
}
sort.Ints(times)
tmedian := times[len(times)/2]
speed := (float64(chunks.length) / float64(tmedian)) * 1e3
return int(speed)
}
func fmtBenchmark(name string, speed int) {
const maxlen = 25
fmt.Printf("%-*s\t%5d mis\n", maxlen, name, speed)
}
func main() {
data := read("clustered1M.bin.gz")
if !sort.IsSorted(sort.IntSlice(data)) {
panic("test data must be sorted")
}
chunks64 := chunkify64(data)
const ntrials = 100
mis := benchmarkPack(ntrials, chunks64)
fmtBenchmark("BenchmarkDeltaPack64", mis)
mis = benchmarkUnpack(ntrials, chunks64)
fmtBenchmark("BenchmarkDeltaUnPack64", mis)
}