mirror of
https://github.com/dolthub/dolt.git
synced 2026-04-23 13:48:42 -05:00
added benchmark options, node cache
This commit is contained in:
@@ -2,21 +2,41 @@
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
if [ "$1" = "new" ]; then
|
||||
echo "benchmarking with new binary format"
|
||||
export "DOLT_FORMAT_FEATURE_FLAG"=true
|
||||
fi
|
||||
|
||||
SYSBENCH_TEST="oltp_point_select"
|
||||
WORKING_DIR=`mktemp -d`
|
||||
PPROF=0
|
||||
|
||||
TMP_DIR=`mktemp -d`
|
||||
cp ./lua/* "$TMP_DIR"
|
||||
cd "$TMP_DIR"
|
||||
# parse options
|
||||
# superuser.com/questions/186272/
|
||||
while test $# -gt 0
|
||||
do
|
||||
case "$1" in
|
||||
|
||||
echo " "
|
||||
echo "running benchmark $SYSBENCH_TEST in $TMP_DIR"
|
||||
echo " "
|
||||
# benchmark with new NomsBinFmt
|
||||
--new-nbf) export DOLT_FORMAT_FEATURE_FLAG=true
|
||||
;;
|
||||
|
||||
# benchmark with pprof profiling
|
||||
--pprof) PPROF=1
|
||||
;;
|
||||
|
||||
# run dolt single threaded
|
||||
--single) export GOMAXPROCS=1
|
||||
;;
|
||||
|
||||
# specify sysbench benchmark
|
||||
*) SYSBENCH_TEST="$1"
|
||||
;;
|
||||
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# collect custom sysbench scripts
|
||||
cp ./lua/* "$WORKING_DIR"
|
||||
cd "$WORKING_DIR"
|
||||
|
||||
# make a sql-server config file
|
||||
cat <<YAML > dolt-config.yaml
|
||||
log_level: "info"
|
||||
|
||||
@@ -40,15 +60,18 @@ databases:
|
||||
path: "."
|
||||
YAML
|
||||
|
||||
# start a server
|
||||
dolt init
|
||||
dolt sql-server --config="dolt-config.yaml" &
|
||||
SERVER_PID="$!"
|
||||
|
||||
# stop it if it crashes
|
||||
cleanup() {
|
||||
kill -15 "$SERVER_PID"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# setup benchmark
|
||||
sleep 1
|
||||
sysbench \
|
||||
--mysql-host="0.0.0.0" \
|
||||
@@ -58,14 +81,26 @@ sysbench \
|
||||
|
||||
# restart server to isolate bench run
|
||||
kill -15 "$SERVER_PID"
|
||||
dolt sql-server --config="dolt-config.yaml" &
|
||||
SERVER_PID="$!"
|
||||
|
||||
# maybe run with pprof
|
||||
if [ "$PPROF" -eq 1 ]; then
|
||||
dolt --prof cpu sql-server --config="dolt-config.yaml" &
|
||||
else
|
||||
dolt sql-server --config="dolt-config.yaml" &
|
||||
fi
|
||||
SERVER_PID="$!"
|
||||
sleep 1
|
||||
|
||||
|
||||
# run benchmark
|
||||
echo "benchmark $SYSBENCH_TEST starting at $WORKING_DIR"
|
||||
|
||||
sysbench \
|
||||
--mysql-host="0.0.0.0" \
|
||||
--mysql-user="user" \
|
||||
--mysql-password="pass" \
|
||||
"$SYSBENCH_TEST" run
|
||||
|
||||
echo "benchmark complete at $TMP_DIR"
|
||||
echo "benchmark $SYSBENCH_TEST complete at $WORKING_DIR"
|
||||
echo "DOLT_FORMAT_FEATURE_FLAG='$DOLT_FORMAT_FEATURE_FLAG'"
|
||||
echo ""
|
||||
|
||||
@@ -41,6 +41,10 @@ func (c Chunk) Hash() hash.Hash {
|
||||
return c.r
|
||||
}
|
||||
|
||||
func (c Chunk) Size() int {
|
||||
return len(c.data)
|
||||
}
|
||||
|
||||
func (c Chunk) Data() []byte {
|
||||
return c.data
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ package prolly
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/dolthub/dolt/go/store/types"
|
||||
|
||||
@@ -24,6 +26,10 @@ import (
|
||||
"github.com/dolthub/dolt/go/store/pool"
|
||||
)
|
||||
|
||||
const (
|
||||
cacheSize = 256 * 1024 * 1024
|
||||
)
|
||||
|
||||
// NodeStore reads and writes prolly tree Nodes.
|
||||
type NodeStore interface {
|
||||
|
||||
@@ -42,29 +48,48 @@ type NodeStore interface {
|
||||
|
||||
type nodeStore struct {
|
||||
store chunks.ChunkStore
|
||||
cache *chunkCache
|
||||
bp pool.BuffPool
|
||||
}
|
||||
|
||||
var _ NodeStore = nodeStore{}
|
||||
|
||||
var sharedCache = newChunkCache(cacheSize)
|
||||
|
||||
var sharedPool = pool.NewBuffPool()
|
||||
|
||||
// NewNodeStore makes a new NodeStore.
|
||||
func NewNodeStore(cs chunks.ChunkStore) NodeStore {
|
||||
return nodeStore{store: cs, bp: sharedPool}
|
||||
return nodeStore{
|
||||
store: cs,
|
||||
cache: sharedCache,
|
||||
bp: sharedPool,
|
||||
}
|
||||
}
|
||||
|
||||
// Read implements NodeStore.
|
||||
func (ns nodeStore) Read(ctx context.Context, ref hash.Hash) (Node, error) {
|
||||
c, ok := ns.cache.get(ref)
|
||||
if ok {
|
||||
return c.Data(), nil
|
||||
}
|
||||
|
||||
c, err := ns.store.Get(ctx, ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ns.cache.insert(c)
|
||||
return c.Data(), err
|
||||
}
|
||||
|
||||
// Write implements NodeStore.
|
||||
func (ns nodeStore) Write(ctx context.Context, nd Node) (hash.Hash, error) {
|
||||
c := chunks.NewChunk(nd)
|
||||
err := ns.store.Put(ctx, c)
|
||||
return c.Hash(), err
|
||||
if err := ns.store.Put(ctx, c); err != nil {
|
||||
return hash.Hash{}, err
|
||||
}
|
||||
ns.cache.insert(c)
|
||||
return c.Hash(), nil
|
||||
}
|
||||
|
||||
// Pool implements NodeStore.
|
||||
@@ -76,3 +101,175 @@ func (ns nodeStore) Format() *types.NomsBinFormat {
|
||||
// todo(andy): read from |ns.store|
|
||||
return types.Format_DOLT_1
|
||||
}
|
||||
|
||||
type centry struct {
|
||||
c chunks.Chunk
|
||||
i int
|
||||
prev *centry
|
||||
next *centry
|
||||
}
|
||||
|
||||
type chunkCache struct {
|
||||
mu *sync.Mutex
|
||||
chunks map[hash.Hash]*centry
|
||||
head *centry
|
||||
sz int
|
||||
maxSz int
|
||||
rev int
|
||||
}
|
||||
|
||||
func newChunkCache(maxSize int) *chunkCache {
|
||||
return &chunkCache{
|
||||
&sync.Mutex{},
|
||||
make(map[hash.Hash]*centry),
|
||||
nil,
|
||||
0,
|
||||
maxSize,
|
||||
0,
|
||||
}
|
||||
}
|
||||
|
||||
func removeFromCList(e *centry) {
|
||||
e.prev.next = e.next
|
||||
e.next.prev = e.prev
|
||||
e.prev = e
|
||||
e.next = e
|
||||
}
|
||||
|
||||
func (mc *chunkCache) moveToFront(e *centry) {
|
||||
e.i = mc.rev
|
||||
mc.rev++
|
||||
if mc.head == e {
|
||||
return
|
||||
}
|
||||
if mc.head != nil {
|
||||
removeFromCList(e)
|
||||
e.next = mc.head
|
||||
e.prev = mc.head.prev
|
||||
mc.head.prev = e
|
||||
e.prev.next = e
|
||||
}
|
||||
mc.head = e
|
||||
}
|
||||
|
||||
func (mc *chunkCache) get(h hash.Hash) (chunks.Chunk, bool) {
|
||||
mc.mu.Lock()
|
||||
defer mc.mu.Unlock()
|
||||
if e, ok := mc.chunks[h]; ok {
|
||||
mc.moveToFront(e)
|
||||
return e.c, true
|
||||
} else {
|
||||
return chunks.EmptyChunk, false
|
||||
}
|
||||
}
|
||||
|
||||
func (mc *chunkCache) getMany(hs hash.HashSet) ([]chunks.Chunk, hash.HashSet) {
|
||||
mc.mu.Lock()
|
||||
defer mc.mu.Unlock()
|
||||
absent := make(map[hash.Hash]struct{})
|
||||
var found []chunks.Chunk
|
||||
for h, _ := range hs {
|
||||
if e, ok := mc.chunks[h]; ok {
|
||||
mc.moveToFront(e)
|
||||
found = append(found, e.c)
|
||||
} else {
|
||||
absent[h] = struct{}{}
|
||||
}
|
||||
}
|
||||
return found, absent
|
||||
}
|
||||
|
||||
func (mc *chunkCache) insert(c chunks.Chunk) {
|
||||
mc.mu.Lock()
|
||||
defer mc.mu.Unlock()
|
||||
mc.addIfAbsent(c)
|
||||
}
|
||||
|
||||
func (mc *chunkCache) insertMany(cs []chunks.Chunk) {
|
||||
mc.mu.Lock()
|
||||
defer mc.mu.Unlock()
|
||||
for _, c := range cs {
|
||||
mc.addIfAbsent(c)
|
||||
}
|
||||
}
|
||||
|
||||
func (mc *chunkCache) addIfAbsent(c chunks.Chunk) {
|
||||
if e, ok := mc.chunks[c.Hash()]; !ok {
|
||||
e := ¢ry{c, 0, nil, nil}
|
||||
e.next = e
|
||||
e.prev = e
|
||||
mc.moveToFront(e)
|
||||
mc.chunks[c.Hash()] = e
|
||||
mc.sz += c.Size()
|
||||
mc.shrinkToMaxSz()
|
||||
} else {
|
||||
mc.moveToFront(e)
|
||||
}
|
||||
}
|
||||
|
||||
func (mc *chunkCache) Len() int {
|
||||
mc.mu.Lock()
|
||||
defer mc.mu.Unlock()
|
||||
return len(mc.chunks)
|
||||
}
|
||||
|
||||
func (mc *chunkCache) Size() int {
|
||||
mc.mu.Lock()
|
||||
defer mc.mu.Unlock()
|
||||
return mc.sz
|
||||
}
|
||||
|
||||
func (mc *chunkCache) shrinkToMaxSz() {
|
||||
for mc.sz > mc.maxSz {
|
||||
if mc.head != nil {
|
||||
t := mc.head.prev
|
||||
removeFromCList(t)
|
||||
if t == mc.head {
|
||||
mc.head = nil
|
||||
}
|
||||
delete(mc.chunks, t.c.Hash())
|
||||
mc.sz -= t.c.Size()
|
||||
} else {
|
||||
panic("cache is empty but cache size is > than max size")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (mc *chunkCache) sanityCheck() {
|
||||
if mc.head != nil {
|
||||
p := mc.head.next
|
||||
i := 1
|
||||
sz := mc.head.c.Size()
|
||||
lasti := mc.head.i
|
||||
for p != mc.head {
|
||||
i++
|
||||
sz += p.c.Size()
|
||||
if p.i >= lasti {
|
||||
panic("encountered lru list entry with higher rev later in the list.")
|
||||
}
|
||||
p = p.next
|
||||
}
|
||||
if i != len(mc.chunks) {
|
||||
panic(fmt.Sprintf("cache lru list has different size than cache.chunks. %d vs %d", i, len(mc.chunks)))
|
||||
}
|
||||
if sz != mc.sz {
|
||||
panic("entries reachable from lru list have different size than cache.sz.")
|
||||
}
|
||||
j := 1
|
||||
p = mc.head.prev
|
||||
for p != mc.head {
|
||||
j++
|
||||
p = p.prev
|
||||
}
|
||||
if j != i {
|
||||
panic("length of list backwards is not equal to length of list forward")
|
||||
}
|
||||
} else {
|
||||
if len(mc.chunks) != 0 {
|
||||
panic("lru list is empty but mc.chunks is not")
|
||||
}
|
||||
if mc.sz != 0 {
|
||||
panic("lru list is empty but mc.sz is not 0")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user