-
Notifications
You must be signed in to change notification settings - Fork 268
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Fast storage optimization for queries and iterations #468
Changes from 89 commits
0d91ad8
4040da1
3740d0b
e175568
24cb701
1c0188d
67f000a
ab5f2cd
3618100
44c6035
19fe40d
fdfe996
8b5b9a7
db293f9
606b56f
36f0760
36b30a1
2a5c19b
5e942a1
2dcf439
1dfc795
2e4daa7
6f4630c
599937d
348d7be
ec07dd1
ffb4c30
6a1d2d7
1db02b3
f3dcb71
0a765bb
b8a9b0a
28da34c
0a0ba33
5bf8c81
41c1827
5fb6d2b
c8def88
eca5e6c
7a5c131
f9ab00e
2f54c55
ff5a3ed
754e8e8
f8c085b
4064369
9abde82
35cacc4
0e09c6b
51b1f33
e773a16
4120794
dfd175e
c6d015d
4d9facc
f2c529c
9b532aa
dc316b9
b04993d
d4b6e01
649387d
ca9904b
9f62bfb
3ca6772
2fb41e8
521d596
176993d
4bb3ecb
19d5195
eb8ae54
7678a17
d98284a
c5bf2e5
d1e999a
69f84a8
5a67677
c925b01
313615e
08cde5a
b5f17cc
accf09d
ac7bc8b
cbfd88a
b0ea918
45e1ed4
611e18c
73cc200
ff8d21c
1e62146
d27a0c3
7009a9c
e4ed678
34d4070
3e064e0
3433ef1
3783679
6bc3fe6
4fe1d9f
484e5a1
81ac12e
57dbc38
b016408
eefb4e7
80b2309
e47a345
a124043
eebf868
34555e3
141d98d
543206b
e44a79a
1236c4c
98d02eb
2feedb7
b5fd3db
67b66c0
69129bf
b4f8932
71e959a
b0d8cdf
f660a25
f56de3a
ff9f32d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,3 +12,6 @@ cpu*.out | |
mem*.out | ||
cpu*.pdf | ||
mem*.pdf | ||
|
||
# IDE files | ||
.idea/* |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -57,21 +57,93 @@ func commitTree(b *testing.B, t *iavl.MutableTree) { | |
} | ||
} | ||
|
||
func runQueries(b *testing.B, t *iavl.MutableTree, keyLen int) { | ||
// queries random keys against live state. Keys are almost certainly not in the tree. | ||
func runQueriesFast(b *testing.B, t *iavl.MutableTree, keyLen int) { | ||
require.True(b, t.IsFastCacheEnabled()) | ||
for i := 0; i < b.N; i++ { | ||
q := randBytes(keyLen) | ||
t.Get(q) | ||
} | ||
} | ||
|
||
func runKnownQueries(b *testing.B, t *iavl.MutableTree, keys [][]byte) { | ||
// queries keys that are known to be in state | ||
func runKnownQueriesFast(b *testing.B, t *iavl.MutableTree, keys [][]byte) { | ||
require.True(b, t.IsFastCacheEnabled()) // to ensure fast storage is enabled | ||
l := int32(len(keys)) | ||
for i := 0; i < b.N; i++ { | ||
q := keys[rand.Int31n(l)] | ||
t.Get(q) | ||
} | ||
} | ||
|
||
func runQueriesSlow(b *testing.B, t *iavl.MutableTree, keyLen int) { | ||
b.StopTimer() | ||
// Save version to get an old immutable tree to query against, | ||
// Fast storage is not enabled on old tree versions, allowing us to bench the desired behavior. | ||
_, version, err := t.SaveVersion() | ||
require.NoError(b, err) | ||
|
||
itree, err := t.GetImmutable(version - 1) | ||
require.NoError(b, err) | ||
require.False(b, itree.IsFastCacheEnabled()) // to ensure fast storage is not enabled | ||
|
||
b.StartTimer() | ||
for i := 0; i < b.N; i++ { | ||
q := randBytes(keyLen) | ||
itree.GetWithIndex(q) | ||
} | ||
} | ||
|
||
func runKnownQueriesSlow(b *testing.B, t *iavl.MutableTree, keys [][]byte) { | ||
b.StopTimer() | ||
// Save version to get an old immutable tree to query against, | ||
// Fast storage is not enabled on old tree versions, allowing us to bench the desired behavior. | ||
_, version, err := t.SaveVersion() | ||
require.NoError(b, err) | ||
|
||
itree, err := t.GetImmutable(version - 1) | ||
require.NoError(b, err) | ||
require.False(b, itree.IsFastCacheEnabled()) // to ensure fast storage is not enabled | ||
b.StartTimer() | ||
l := int32(len(keys)) | ||
for i := 0; i < b.N; i++ { | ||
q := keys[rand.Int31n(l)] | ||
itree.GetWithIndex(q) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should also perhaps have an assertion so that if the result returned is nil that we crash so index, value := itree.GetWithIndex(q)
require.True(b, index >= 0, "the index must not be negative")
require.NotNil(b, value, "the value should exist") |
||
} | ||
} | ||
|
||
func runIterationFast(b *testing.B, t *iavl.MutableTree, expectedSize int) { | ||
require.True(b, t.IsFastCacheEnabled()) // to ensure fast storage is enabled | ||
for i := 0; i < b.N; i++ { | ||
itr := t.ImmutableTree.Iterator(nil, nil, false) | ||
iterate(b, itr, expectedSize) | ||
itr.Close() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. require.Nil(t, itr.Close(), ".Close should not error out") |
||
} | ||
} | ||
|
||
func runIterationSlow(b *testing.B, t *iavl.MutableTree, expectedSize int) { | ||
for i := 0; i < b.N; i++ { | ||
itr := iavl.NewIterator(nil, nil, false, t.ImmutableTree) // create slow iterator directly | ||
iterate(b, itr, expectedSize) | ||
itr.Close() | ||
tac0turtle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
|
||
func iterate(b *testing.B, itr db.Iterator, expectedSize int) { | ||
b.StartTimer() | ||
keyValuePairs := make([][][]byte, 0, expectedSize) | ||
for i := 0; i < expectedSize && itr.Valid(); i++ { | ||
itr.Next() | ||
keyValuePairs = append(keyValuePairs, [][]byte{itr.Key(), itr.Value()}) | ||
} | ||
b.StopTimer() | ||
if len(keyValuePairs) != expectedSize { | ||
b.Errorf("iteration count mismatch: %d != %d", len(keyValuePairs), expectedSize) | ||
} else { | ||
tac0turtle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
b.Logf("completed %d iterations", len(keyValuePairs)) | ||
} | ||
} | ||
|
||
// func runInsert(b *testing.B, t *iavl.MutableTree, keyLen, dataLen, blockSize int) *iavl.MutableTree { | ||
// for i := 1; i <= b.N; i++ { | ||
// t.Set(randBytes(keyLen), randBytes(dataLen)) | ||
|
@@ -132,7 +204,7 @@ func runBlock(b *testing.B, t *iavl.MutableTree, keyLen, dataLen, blockSize int, | |
data := randBytes(dataLen) | ||
|
||
// perform query and write on check and then real | ||
// check.Get(key) | ||
// check.GetFast(key) | ||
// check.Set(key, data) | ||
real.Get(key) | ||
real.Set(key, data) | ||
|
@@ -287,14 +359,33 @@ func runSuite(b *testing.B, d db.DB, initSize, blockSize, keyLen, dataLen int) { | |
|
||
b.ResetTimer() | ||
|
||
b.Run("query-miss", func(sub *testing.B) { | ||
b.Run("query-no-in-tree-guarantee-fast", func(sub *testing.B) { | ||
sub.ReportAllocs() | ||
runQueriesFast(sub, t, keyLen) | ||
}) | ||
b.Run("query-no-in-tree-guarantee-slow", func(sub *testing.B) { | ||
sub.ReportAllocs() | ||
runQueriesSlow(sub, t, keyLen) | ||
}) | ||
// | ||
b.Run("query-hits-fast", func(sub *testing.B) { | ||
sub.ReportAllocs() | ||
runKnownQueriesFast(sub, t, keys) | ||
}) | ||
b.Run("query-hits-slow", func(sub *testing.B) { | ||
sub.ReportAllocs() | ||
runKnownQueriesSlow(sub, t, keys) | ||
}) | ||
// | ||
b.Run("iteration-fast", func(sub *testing.B) { | ||
sub.ReportAllocs() | ||
runQueries(sub, t, keyLen) | ||
runIterationFast(sub, t, initSize) | ||
}) | ||
b.Run("query-hits", func(sub *testing.B) { | ||
b.Run("iteration-slow", func(sub *testing.B) { | ||
sub.ReportAllocs() | ||
runKnownQueries(sub, t, keys) | ||
runIterationSlow(sub, t, initSize) | ||
}) | ||
// | ||
b.Run("update", func(sub *testing.B) { | ||
sub.ReportAllocs() | ||
t = runUpdate(sub, t, dataLen, blockSize, keys) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -52,8 +52,8 @@ func setupExportTreeRandom(t *testing.T) *ImmutableTree { | |
keySize = 16 | ||
valueSize = 16 | ||
|
||
versions = 32 // number of versions to generate | ||
versionOps = 4096 // number of operations (create/update/delete) per version | ||
versions = 8 // number of versions to generate | ||
versionOps = 1024 // number of operations (create/update/delete) per version | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why changing this? |
||
updateRatio = 0.4 // ratio of updates out of all operations | ||
deleteRatio = 0.2 // ratio of deletes out of all operations | ||
) | ||
|
@@ -211,8 +211,8 @@ func TestExporter_Import(t *testing.T) { | |
require.Equal(t, tree.Version(), newTree.Version(), "Tree version mismatch") | ||
|
||
tree.Iterate(func(key, value []byte) bool { | ||
index, _ := tree.Get(key) | ||
newIndex, newValue := newTree.Get(key) | ||
index, _ := tree.GetWithIndex(key) | ||
newIndex, newValue := newTree.GetWithIndex(key) | ||
require.Equal(t, index, newIndex, "Index mismatch for key %v", key) | ||
require.Equal(t, value, newValue, "Value mismatch for key %v", key) | ||
return false | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please remove the 3 lines above. We are targetting master, so #468 should go under Unreleased