arnaucube
arnaucube/arboPublic Self-hosted repository
Update CaseB to handle repeated keys cases

- Update CaseB to handle repeated keys cases
- Add test for AddBatch/CaseB with repeated keys
- AddBatch-tests abstract code reusage
arnaucube root@arnaucube.com
Sat, 08 May 2021 14:52:15 +0200
commit

0eda440d93a15809a747318fe630c63838f1b014

parent

0dee3bc050b6f606eef13c9cf7d04b013acd074b

2 files changed, 100 insertions(+), 21 deletions(-)

jump to
M addbatch.go → addbatch.go

@@ -309,25 +309,27 @@ if err != nil {

return nil, nil, err } // add already existing key-values to the inputted key-values - kvs = append(kvs, aKvs...) + // kvs = append(kvs, aKvs...) + kvs, invalids := combineInKVSet(aKvs, kvs) // proceed with CASE A sortKvs(kvs) // cutPowerOfTwo, the excedent add it as normal Tree.Add kvsP2, kvsNonP2 := cutPowerOfTwo(kvs) - var invalids []int + var invalids2 []int if nCPU > 1 { - invalids, err = t.buildTreeBottomUp(nCPU, kvsP2) + invalids2, err = t.buildTreeBottomUp(nCPU, kvsP2) if err != nil { return nil, nil, err } } else { - invalids, err = t.buildTreeBottomUpSingleThread(kvsP2) + invalids2, err = t.buildTreeBottomUpSingleThread(kvsP2) if err != nil { return nil, nil, err } } + invalids = append(invalids, invalids2...) // return the excedents which will be added at the full tree at the end return invalids, kvsNonP2, nil }

@@ -357,7 +359,7 @@

// 3. do CASE B (with 1 cpu) for each key at level L _, bucketExcedents, err := bucketTree.caseB(1, l, buckets[cpu]) if err != nil { - panic(err) + panic(err) // TODO WIP // return nil, err } excedentsInBucket[cpu] = bucketExcedents

@@ -718,6 +720,30 @@ break

} } return res +} + +// combineInKVSet combines two kv array in one single array without repeated +// keys. +func combineInKVSet(base, toAdd []kv) ([]kv, []int) { + // TODO this is a naive version, this will be implemented in a more + // efficient way or through maps, or through sorted binary search + r := base + var invalids []int + for i := 0; i < len(toAdd); i++ { + e := false + // check if toAdd[i] exists in the base set + for j := 0; j < len(base); j++ { + if bytes.Equal(toAdd[i].k, base[j].k) { + e = true + } + } + if !e { + r = append(r, toAdd[i]) + } else { + invalids = append(invalids, toAdd[i].pos) + } + } + return r, invalids } // func computeSimpleAddCost(nLeafs int) int {
M addbatch_test.go → addbatch_test.go

@@ -11,6 +11,15 @@ qt "github.com/frankban/quicktest"

"github.com/iden3/go-merkletree/db/memory" ) +var debug = true + +func debugTime(descr string, time1, time2 time.Duration) { + if debug { + fmt.Printf("%s was %f times faster than without AddBatch\n", + descr, float64(time1)/float64(time2)) + } +} + func testInit(c *qt.C, n int) (*Tree, *Tree) { tree1, err := NewTree(memory.NewMemoryStorage(), 100, HashFunctionPoseidon) c.Assert(err, qt.IsNil)

@@ -35,12 +44,6 @@ }

return tree1, tree2 } -func ratio(t1, t2 time.Duration) float64 { - a := float64(t1) - b := float64(t2) - return (a / b) -} - func TestAddBatchCaseA(t *testing.T) { c := qt.New(t)

@@ -75,8 +78,7 @@ start = time.Now()

indexes, err := tree2.AddBatchOpt(keys, values) c.Assert(err, qt.IsNil) time2 := time.Since(start) - fmt.Printf("CASE A, AddBatch was %f times faster than without AddBatch\n", - ratio(time1, time2)) + debugTime("CASE A, AddBatch", time1, time2) c.Check(len(indexes), qt.Equals, 0) // check that both trees roots are equal

@@ -149,14 +151,68 @@ start = time.Now()

indexes, err := tree2.AddBatchOpt(keys, values) c.Assert(err, qt.IsNil) time2 := time.Since(start) - fmt.Printf("CASE B, AddBatch was %f times faster than without AddBatch\n", - ratio(time1, time2)) + debugTime("CASE B, AddBatch", time1, time2) c.Check(len(indexes), qt.Equals, 0) // check that both trees roots are equal c.Check(tree2.Root(), qt.DeepEquals, tree1.Root()) } +func TestAddBatchCaseBRepeatedLeafs(t *testing.T) { + c := qt.New(t) + + nLeafs := 1024 + initialNLeafs := 99 // TMP TODO use const minLeafsThreshold-1 once ready + + tree1, tree2 := testInit(c, initialNLeafs) + + for i := initialNLeafs; i < nLeafs; i++ { + k := BigIntToBytes(big.NewInt(int64(i))) + v := BigIntToBytes(big.NewInt(int64(i * 2))) + if err := tree1.Add(k, v); err != nil { + t.Fatal(err) + } + } + + // prepare the key-values to be added, including already existing keys + var keys, values [][]byte + for i := 0; i < nLeafs; i++ { + k := BigIntToBytes(big.NewInt(int64(i))) + v := BigIntToBytes(big.NewInt(int64(i * 2))) + keys = append(keys, k) + values = append(values, v) + } + indexes, err := tree2.AddBatchOpt(keys, values) + c.Assert(err, qt.IsNil) + c.Check(len(indexes), qt.Equals, initialNLeafs) + + // check that both trees roots are equal + c.Check(tree2.Root(), qt.DeepEquals, tree1.Root()) +} + +func TestCombineInKVSet(t *testing.T) { + c := qt.New(t) + + var a, b, expected []kv + for i := 0; i < 10; i++ { + k := BigIntToBytes(big.NewInt(int64(i))) + kv := kv{k: k} + if i < 7 { + a = append(a, kv) + } + if i >= 4 { + b = append(b, kv) + } + expected = append(expected, kv) + } + + r, invalids := combineInKVSet(a, b) + for i := 0; i < len(r); i++ { + c.Assert(r[i].k, qt.DeepEquals, expected[i].k) + } + c.Assert(len(invalids), qt.Equals, 7-4) +} + func TestGetKeysAtLevel(t *testing.T) { c := qt.New(t)

@@ -315,8 +371,7 @@ start = time.Now()

indexes, err := tree2.AddBatchOpt(keys, values) c.Assert(err, qt.IsNil) time2 := time.Since(start) - fmt.Printf("CASE C, AddBatch was %f times faster than without AddBatch\n", - ratio(time1, time2)) + debugTime("CASE C, AddBatch", time1, time2) c.Check(len(indexes), qt.Equals, 0) // check that both trees roots are equal

@@ -353,8 +408,7 @@ start = time.Now()

indexes, err := tree2.AddBatchOpt(keys, values) c.Assert(err, qt.IsNil) time2 := time.Since(start) - fmt.Printf("CASE D, AddBatch was %f times faster than without AddBatch\n", - ratio(time1, time2)) + debugTime("CASE D, AddBatch", time1, time2) c.Check(len(indexes), qt.Equals, 0) // check that both trees roots are equal

@@ -411,8 +465,7 @@ start = time.Now()

indexes, err := tree2.AddBatchOpt(keys, values) c.Assert(err, qt.IsNil) time2 := time.Since(start) - fmt.Printf("CASE E, AddBatch was %f times faster than without AddBatch\n", - ratio(time1, time2)) + debugTime("CASE E, AddBatch", time1, time2) c.Check(len(indexes), qt.Equals, 0) // check that both trees roots are equal