added benchmark options, node cache

This commit is contained in:
Andy Arthur
2022-01-13 20:03:40 -08:00
parent ea91335491
commit be0b12154d
3 changed files with 253 additions and 17 deletions
@@ -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 ""
+4
View File
@@ -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
}
+200 -3
View File
@@ -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 := &centry{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")
}
}
}