Add dbgStats metrics

Add dbgStats metrics to analyze number of Hashes, db Gets, and db Puts.

Current benchmarks:
```
CASE A, AddBatch was 8.841700 times faster than without AddBatch
	nCPU: 4, nLeafs: 1024, hash: Poseidon, db: memory
	dbgStats(hash: 2.044k, dbGet: 1, dbPut: 2.049k)
CASE B, AddBatch was 7.678766 times faster than without AddBatch
	nCPU: 4, nLeafs: 1024, hash: Poseidon, db: memory
	dbgStats(hash: 2.044k, dbGet: 199, dbPut: 2.049k)
CASE C, AddBatch was 8.401087 times faster than without AddBatch
	nCPU: 4, nLeafs: 1024, hash: Poseidon, db: memory
	dbgStats(hash: 2.044k, dbGet: 207, dbPut: 2.049k)
CASE D, AddBatch was 2.466346 times faster than without AddBatch
	nCPU: 4, nLeafs: 4096, hash: Poseidon, db: memory
	dbgStats(hash: 33.884k, dbGet: 30.697k, dbPut: 33.889k)
CASE E, AddBatch was 1.958160 times faster than without AddBatch
	nCPU: 4, nLeafs: 4096, hash: Poseidon, db: memory
	dbgStats(hash: 41.419k, dbGet: 37.558k, dbPut: 41.874k)
TestAddBatchBench: nCPU: 4, nLeafs: 50000, hash: Blake2b, db: leveldb
	Add loop:	10.089858449s
		dbgStats(hash: 825.285k, dbGet: 788.869k, dbPut: 925.285k)
	AddBatch:	904.647829ms
		dbgStats(hash: 122.458k, dbGet: 1, dbPut: 122.463k)
TestDbgStats
	add in loop    dbgStats(hash: 141.915k, dbGet: 134.602k, dbPut: 161.915k)
	addbatch caseA dbgStats(hash: 24.528k, dbGet: 1, dbPut: 24.533k)
	addbatch caseD dbgStats(hash: 115.506k, dbGet: 97.482k, dbPut: 115.516k)
```
This commit is contained in:
2021-05-23 16:19:04 +02:00
parent 2a57e223ef
commit 4167583b8d
5 changed files with 304 additions and 52 deletions

54
tree.go
View File

@@ -57,7 +57,11 @@ type Tree struct {
root []byte
hashFunction HashFunction
emptyHash []byte
// TODO in the methods that use it, check if emptyHash param is len>0
// (check if it has been initialized)
emptyHash []byte
dbg *dbgStats
}
// NewTree returns a new Tree, if there is a Tree still in the given storage, it
@@ -76,7 +80,7 @@ func NewTree(storage db.Storage, maxLevels int, hash HashFunction) (*Tree, error
return nil, err
}
t.root = t.emptyHash
if err = t.tx.Put(dbKeyRoot, t.root); err != nil {
if err = t.dbPut(dbKeyRoot, t.root); err != nil {
return nil, err
}
if err = t.setNLeafs(0); err != nil {
@@ -132,7 +136,7 @@ func (t *Tree) Add(k, v []byte) error {
return err
}
// store root to db
if err := t.tx.Put(dbKeyRoot, t.root); err != nil {
if err := t.dbPut(dbKeyRoot, t.root); err != nil {
return err
}
// update nLeafs
@@ -156,12 +160,12 @@ func (t *Tree) add(fromLvl int, k, v []byte) error {
return err
}
leafKey, leafValue, err := newLeafValue(t.hashFunction, k, v)
leafKey, leafValue, err := t.newLeafValue(k, v)
if err != nil {
return err
}
if err := t.tx.Put(leafKey, leafValue); err != nil {
if err := t.dbPut(leafKey, leafValue); err != nil {
return err
}
@@ -186,6 +190,7 @@ func (t *Tree) down(newKey, currKey []byte, siblings [][]byte,
if currLvl > t.maxLevels-1 {
return nil, nil, nil, fmt.Errorf("max level")
}
var err error
var currValue []byte
if bytes.Equal(currKey, t.emptyHash) {
@@ -205,6 +210,9 @@ func (t *Tree) down(newKey, currKey []byte, siblings [][]byte,
panic("should not be reached, as the 'if' above should avoid reaching this point") // TMP
case PrefixValueLeaf: // leaf
if bytes.Equal(newKey, currKey) {
// TODO move this error msg to const & add test that
// checks that adding a repeated key this error is
// returned
return nil, nil, nil, fmt.Errorf("key already exists")
}
@@ -275,18 +283,18 @@ func (t *Tree) up(key []byte, siblings [][]byte, path []bool, currLvl, toLvl int
var k, v []byte
var err error
if path[currLvl+toLvl] {
k, v, err = newIntermediate(t.hashFunction, siblings[currLvl], key)
k, v, err = t.newIntermediate(siblings[currLvl], key)
if err != nil {
return nil, err
}
} else {
k, v, err = newIntermediate(t.hashFunction, key, siblings[currLvl])
k, v, err = t.newIntermediate(key, siblings[currLvl])
if err != nil {
return nil, err
}
}
// store k-v to db
if err = t.tx.Put(k, v); err != nil {
if err = t.dbPut(k, v); err != nil {
return nil, err
}
@@ -298,6 +306,11 @@ func (t *Tree) up(key []byte, siblings [][]byte, path []bool, currLvl, toLvl int
return t.up(k, siblings, path, currLvl-1, toLvl)
}
func (t *Tree) newLeafValue(k, v []byte) ([]byte, []byte, error) {
t.dbg.incHash()
return newLeafValue(t.hashFunction, k, v)
}
func newLeafValue(hashFunc HashFunction, k, v []byte) ([]byte, []byte, error) {
leafKey, err := hashFunc.Hash(k, v, []byte{1})
if err != nil {
@@ -326,6 +339,11 @@ func ReadLeafValue(b []byte) ([]byte, []byte) {
return k, v
}
func (t *Tree) newIntermediate(l, r []byte) ([]byte, []byte, error) {
t.dbg.incHash()
return newIntermediate(t.hashFunction, l, r)
}
func newIntermediate(hashFunc HashFunction, l, r []byte) ([]byte, []byte, error) {
b := make([]byte, PrefixValueLen+hashFunc.Len()*2)
b[0] = 2
@@ -392,12 +410,12 @@ func (t *Tree) Update(k, v []byte) error {
return fmt.Errorf("key %s does not exist", hex.EncodeToString(k))
}
leafKey, leafValue, err := newLeafValue(t.hashFunction, k, v)
leafKey, leafValue, err := t.newLeafValue(k, v)
if err != nil {
return err
}
if err := t.tx.Put(leafKey, leafValue); err != nil {
if err := t.dbPut(leafKey, leafValue); err != nil {
return err
}
@@ -413,7 +431,7 @@ func (t *Tree) Update(k, v []byte) error {
t.root = root
// store root to db
if err := t.tx.Put(dbKeyRoot, t.root); err != nil {
if err := t.dbPut(dbKeyRoot, t.root); err != nil {
return err
}
return t.tx.Commit()
@@ -535,7 +553,8 @@ func (t *Tree) Get(k []byte) ([]byte, []byte, error) {
}
leafK, leafV := ReadLeafValue(value)
if !bytes.Equal(k, leafK) {
panic(fmt.Errorf("%s != %s", BytesToBigInt(k), BytesToBigInt(leafK)))
panic(fmt.Errorf("Tree.Get error: keys doesn't match, %s != %s",
BytesToBigInt(k), BytesToBigInt(leafK)))
}
return leafK, leafV, nil
@@ -577,11 +596,20 @@ func CheckProof(hashFunc HashFunction, k, v, root, packedSiblings []byte) (bool,
return false, nil
}
func (t *Tree) dbPut(k, v []byte) error {
if t.tx == nil {
return fmt.Errorf("dbPut error: no db Tx")
}
t.dbg.incDbPut()
return t.tx.Put(k, v)
}
func (t *Tree) dbGet(k []byte) ([]byte, error) {
// if key is empty, return empty as value
if bytes.Equal(k, t.emptyHash) {
return t.emptyHash, nil
}
t.dbg.incDbGet()
v, err := t.db.Get(k)
if err == nil {
@@ -609,7 +637,7 @@ func (t *Tree) incNLeafs(nLeafs int) error {
func (t *Tree) setNLeafs(nLeafs int) error {
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, uint64(nLeafs))
if err := t.tx.Put(dbKeyNLeafs, b); err != nil {
if err := t.dbPut(dbKeyNLeafs, b); err != nil {
return err
}
return nil