Skip to content

Commit bfd9fa6

Browse files
authored
feat: Sequential insertion in indexed trees (#10111)
- Adds sequential (no subtree) insertion in indexed trees - If witnesses are requested, the response includes 2*N witnesses, N for the low leaves and N for the insertions - If no witness is requested, it directly does sparse_batch_update - Updating an item multiple times in the same call is allowed - Uses sequential insertion with witnesses to avoid doing N batch updates of 1 item for the base rollup - Uses sequential insertion without witnesses for syncing the public data tree, to avoid doing 1 batch insertion per TX in the block
1 parent 6fd5fc1 commit bfd9fa6

File tree

19 files changed

+1002
-220
lines changed

19 files changed

+1002
-220
lines changed

barretenberg/cpp/src/barretenberg/benchmark/indexed_tree_bench/indexed_tree.bench.cpp

+163-26
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,79 @@ const size_t MAX_BATCH_SIZE = 64;
2727
template <typename TreeType> void add_values(TreeType& tree, const std::vector<NullifierLeafValue>& values)
2828
{
2929
Signal signal(1);
30-
typename TreeType::AddCompletionCallback completion = [&](const auto&) -> void { signal.signal_level(0); };
30+
bool success = true;
31+
std::string error_message;
32+
typename TreeType::AddCompletionCallback completion = [&](const auto& result) -> void {
33+
success = result.success;
34+
error_message = result.message;
35+
signal.signal_level(0);
36+
};
3137

3238
tree.add_or_update_values(values, completion);
3339
signal.wait_for_level(0);
40+
if (!success) {
41+
throw std::runtime_error(format("Failed to add values: ", error_message));
42+
}
3443
}
3544

3645
template <typename TreeType> void add_values_with_witness(TreeType& tree, const std::vector<NullifierLeafValue>& values)
3746
{
47+
bool success = true;
48+
std::string error_message;
3849
Signal signal(1);
39-
typename TreeType::AddCompletionCallbackWithWitness completion = [&](const auto&) -> void {
50+
typename TreeType::AddCompletionCallbackWithWitness completion = [&](const auto& result) -> void {
51+
success = result.success;
52+
error_message = result.message;
4053
signal.signal_level(0);
4154
};
4255

4356
tree.add_or_update_values(values, completion);
4457
signal.wait_for_level(0);
58+
if (!success) {
59+
throw std::runtime_error(format("Failed to add values with witness: ", error_message));
60+
}
61+
}
62+
63+
template <typename TreeType> void add_values_sequentially(TreeType& tree, const std::vector<NullifierLeafValue>& values)
64+
{
65+
bool success = true;
66+
std::string error_message;
67+
Signal signal(1);
68+
typename TreeType::AddCompletionCallback completion = [&](const auto& result) -> void {
69+
success = result.success;
70+
error_message = result.message;
71+
signal.signal_level(0);
72+
};
73+
74+
tree.add_or_update_values_sequentially(values, completion);
75+
signal.wait_for_level(0);
76+
if (!success) {
77+
throw std::runtime_error(format("Failed to add values sequentially: ", error_message));
78+
}
79+
}
80+
81+
template <typename TreeType>
82+
void add_values_sequentially_with_witness(TreeType& tree, const std::vector<NullifierLeafValue>& values)
83+
{
84+
bool success = true;
85+
std::string error_message;
86+
Signal signal(1);
87+
typename TreeType::AddSequentiallyCompletionCallbackWithWitness completion = [&](const auto& result) -> void {
88+
success = result.success;
89+
error_message = result.message;
90+
signal.signal_level(0);
91+
};
92+
93+
tree.add_or_update_values_sequentially(values, completion);
94+
signal.wait_for_level(0);
95+
if (!success) {
96+
throw std::runtime_error(format("Failed to add values sequentially with witness: ", error_message));
97+
}
4598
}
4699

47-
template <typename TreeType> void multi_thread_indexed_tree_bench(State& state) noexcept
100+
enum InsertionStrategy { SEQUENTIAL, BATCH };
101+
102+
template <typename TreeType, InsertionStrategy strategy> void multi_thread_indexed_tree_bench(State& state) noexcept
48103
{
49104
const size_t batch_size = size_t(state.range(0));
50105
const size_t depth = TREE_DEPTH;
@@ -61,10 +116,14 @@ template <typename TreeType> void multi_thread_indexed_tree_bench(State& state)
61116

62117
const size_t initial_size = 1024 * 16;
63118
std::vector<NullifierLeafValue> initial_batch(initial_size);
64-
for (size_t i = 0; i < batch_size; ++i) {
119+
for (size_t i = 0; i < initial_size; ++i) {
65120
initial_batch[i] = fr(random_engine.get_random_uint256());
66121
}
67-
add_values(tree, initial_batch);
122+
if (strategy == SEQUENTIAL) {
123+
add_values_sequentially(tree, initial_batch);
124+
} else {
125+
add_values(tree, initial_batch);
126+
}
68127

69128
for (auto _ : state) {
70129
state.PauseTiming();
@@ -73,11 +132,15 @@ template <typename TreeType> void multi_thread_indexed_tree_bench(State& state)
73132
values[i] = fr(random_engine.get_random_uint256());
74133
}
75134
state.ResumeTiming();
76-
add_values(tree, values);
135+
if (strategy == SEQUENTIAL) {
136+
add_values_sequentially(tree, values);
137+
} else {
138+
add_values(tree, values);
139+
}
77140
}
78141
}
79142

80-
template <typename TreeType> void single_thread_indexed_tree_bench(State& state) noexcept
143+
template <typename TreeType, InsertionStrategy strategy> void single_thread_indexed_tree_bench(State& state) noexcept
81144
{
82145
const size_t batch_size = size_t(state.range(0));
83146
const size_t depth = TREE_DEPTH;
@@ -94,10 +157,14 @@ template <typename TreeType> void single_thread_indexed_tree_bench(State& state)
94157

95158
const size_t initial_size = 1024 * 16;
96159
std::vector<NullifierLeafValue> initial_batch(initial_size);
97-
for (size_t i = 0; i < batch_size; ++i) {
160+
for (size_t i = 0; i < initial_size; ++i) {
98161
initial_batch[i] = fr(random_engine.get_random_uint256());
99162
}
100-
add_values(tree, initial_batch);
163+
if (strategy == SEQUENTIAL) {
164+
add_values_sequentially(tree, initial_batch);
165+
} else {
166+
add_values(tree, initial_batch);
167+
}
101168

102169
for (auto _ : state) {
103170
state.PauseTiming();
@@ -106,11 +173,16 @@ template <typename TreeType> void single_thread_indexed_tree_bench(State& state)
106173
values[i] = fr(random_engine.get_random_uint256());
107174
}
108175
state.ResumeTiming();
109-
add_values(tree, values);
176+
if (strategy == SEQUENTIAL) {
177+
add_values_sequentially(tree, values);
178+
} else {
179+
add_values(tree, values);
180+
}
110181
}
111182
}
112183

113-
template <typename TreeType> void multi_thread_indexed_tree_with_witness_bench(State& state) noexcept
184+
template <typename TreeType, InsertionStrategy strategy>
185+
void multi_thread_indexed_tree_with_witness_bench(State& state) noexcept
114186
{
115187
const size_t batch_size = size_t(state.range(0));
116188
const size_t depth = TREE_DEPTH;
@@ -127,10 +199,14 @@ template <typename TreeType> void multi_thread_indexed_tree_with_witness_bench(S
127199

128200
const size_t initial_size = 1024 * 16;
129201
std::vector<NullifierLeafValue> initial_batch(initial_size);
130-
for (size_t i = 0; i < batch_size; ++i) {
202+
for (size_t i = 0; i < initial_size; ++i) {
131203
initial_batch[i] = fr(random_engine.get_random_uint256());
132204
}
133-
add_values(tree, initial_batch);
205+
if (strategy == SEQUENTIAL) {
206+
add_values_sequentially(tree, initial_batch);
207+
} else {
208+
add_values(tree, initial_batch);
209+
}
134210

135211
for (auto _ : state) {
136212
state.PauseTiming();
@@ -139,11 +215,16 @@ template <typename TreeType> void multi_thread_indexed_tree_with_witness_bench(S
139215
values[i] = fr(random_engine.get_random_uint256());
140216
}
141217
state.ResumeTiming();
142-
add_values_with_witness(tree, values);
218+
if (strategy == SEQUENTIAL) {
219+
add_values_sequentially_with_witness(tree, values);
220+
} else {
221+
add_values_with_witness(tree, values);
222+
}
143223
}
144224
}
145225

146-
template <typename TreeType> void single_thread_indexed_tree_with_witness_bench(State& state) noexcept
226+
template <typename TreeType, InsertionStrategy strategy>
227+
void single_thread_indexed_tree_with_witness_bench(State& state) noexcept
147228
{
148229
const size_t batch_size = size_t(state.range(0));
149230
const size_t depth = TREE_DEPTH;
@@ -160,10 +241,14 @@ template <typename TreeType> void single_thread_indexed_tree_with_witness_bench(
160241

161242
const size_t initial_size = 1024 * 16;
162243
std::vector<NullifierLeafValue> initial_batch(initial_size);
163-
for (size_t i = 0; i < batch_size; ++i) {
244+
for (size_t i = 0; i < initial_size; ++i) {
164245
initial_batch[i] = fr(random_engine.get_random_uint256());
165246
}
166-
add_values(tree, initial_batch);
247+
if (strategy == SEQUENTIAL) {
248+
add_values_sequentially(tree, initial_batch);
249+
} else {
250+
add_values(tree, initial_batch);
251+
}
167252

168253
for (auto _ : state) {
169254
state.PauseTiming();
@@ -172,53 +257,105 @@ template <typename TreeType> void single_thread_indexed_tree_with_witness_bench(
172257
values[i] = fr(random_engine.get_random_uint256());
173258
}
174259
state.ResumeTiming();
175-
add_values_with_witness(tree, values);
260+
if (strategy == SEQUENTIAL) {
261+
add_values_sequentially_with_witness(tree, values);
262+
} else {
263+
add_values_with_witness(tree, values);
264+
}
176265
}
177266
}
178267

179-
BENCHMARK(single_thread_indexed_tree_with_witness_bench<Poseidon2>)
268+
BENCHMARK(single_thread_indexed_tree_with_witness_bench<Poseidon2, BATCH>)
180269
->Unit(benchmark::kMillisecond)
181270
->RangeMultiplier(2)
182271
->Range(2, MAX_BATCH_SIZE)
183272
->Iterations(1000);
184273

185-
BENCHMARK(single_thread_indexed_tree_with_witness_bench<Poseidon2>)
274+
BENCHMARK(single_thread_indexed_tree_with_witness_bench<Poseidon2, BATCH>)
186275
->Unit(benchmark::kMillisecond)
187276
->RangeMultiplier(2)
188277
->Range(512, 8192)
189278
->Iterations(10);
190279

191-
BENCHMARK(multi_thread_indexed_tree_with_witness_bench<Poseidon2>)
280+
BENCHMARK(single_thread_indexed_tree_with_witness_bench<Poseidon2, SEQUENTIAL>)
192281
->Unit(benchmark::kMillisecond)
193282
->RangeMultiplier(2)
194283
->Range(2, MAX_BATCH_SIZE)
195284
->Iterations(1000);
196285

197-
BENCHMARK(multi_thread_indexed_tree_with_witness_bench<Poseidon2>)
286+
BENCHMARK(single_thread_indexed_tree_with_witness_bench<Poseidon2, SEQUENTIAL>)
198287
->Unit(benchmark::kMillisecond)
199288
->RangeMultiplier(2)
200289
->Range(512, 8192)
201290
->Iterations(10);
202291

203-
BENCHMARK(single_thread_indexed_tree_bench<Poseidon2>)
292+
BENCHMARK(multi_thread_indexed_tree_with_witness_bench<Poseidon2, BATCH>)
204293
->Unit(benchmark::kMillisecond)
205294
->RangeMultiplier(2)
206295
->Range(2, MAX_BATCH_SIZE)
207296
->Iterations(1000);
208297

209-
BENCHMARK(single_thread_indexed_tree_bench<Poseidon2>)
298+
BENCHMARK(multi_thread_indexed_tree_with_witness_bench<Poseidon2, BATCH>)
210299
->Unit(benchmark::kMillisecond)
211300
->RangeMultiplier(2)
212301
->Range(512, 8192)
213302
->Iterations(10);
214303

215-
BENCHMARK(multi_thread_indexed_tree_bench<Poseidon2>)
304+
BENCHMARK(multi_thread_indexed_tree_with_witness_bench<Poseidon2, SEQUENTIAL>)
305+
->Unit(benchmark::kMillisecond)
306+
->RangeMultiplier(2)
307+
->Range(2, MAX_BATCH_SIZE)
308+
->Iterations(1000);
309+
310+
BENCHMARK(multi_thread_indexed_tree_with_witness_bench<Poseidon2, SEQUENTIAL>)
311+
->Unit(benchmark::kMillisecond)
312+
->RangeMultiplier(2)
313+
->Range(512, 8192)
314+
->Iterations(10);
315+
316+
BENCHMARK(single_thread_indexed_tree_bench<Poseidon2, BATCH>)
317+
->Unit(benchmark::kMillisecond)
318+
->RangeMultiplier(2)
319+
->Range(2, MAX_BATCH_SIZE)
320+
->Iterations(1000);
321+
322+
BENCHMARK(single_thread_indexed_tree_bench<Poseidon2, BATCH>)
323+
->Unit(benchmark::kMillisecond)
324+
->RangeMultiplier(2)
325+
->Range(512, 8192)
326+
->Iterations(10);
327+
328+
BENCHMARK(single_thread_indexed_tree_bench<Poseidon2, SEQUENTIAL>)
329+
->Unit(benchmark::kMillisecond)
330+
->RangeMultiplier(2)
331+
->Range(2, MAX_BATCH_SIZE)
332+
->Iterations(1000);
333+
334+
BENCHMARK(single_thread_indexed_tree_bench<Poseidon2, SEQUENTIAL>)
335+
->Unit(benchmark::kMillisecond)
336+
->RangeMultiplier(2)
337+
->Range(512, 8192)
338+
->Iterations(10);
339+
340+
BENCHMARK(multi_thread_indexed_tree_bench<Poseidon2, BATCH>)
341+
->Unit(benchmark::kMillisecond)
342+
->RangeMultiplier(2)
343+
->Range(2, MAX_BATCH_SIZE)
344+
->Iterations(1000);
345+
346+
BENCHMARK(multi_thread_indexed_tree_bench<Poseidon2, BATCH>)
347+
->Unit(benchmark::kMillisecond)
348+
->RangeMultiplier(2)
349+
->Range(512, 8192)
350+
->Iterations(100);
351+
352+
BENCHMARK(multi_thread_indexed_tree_bench<Poseidon2, SEQUENTIAL>)
216353
->Unit(benchmark::kMillisecond)
217354
->RangeMultiplier(2)
218355
->Range(2, MAX_BATCH_SIZE)
219356
->Iterations(1000);
220357

221-
BENCHMARK(multi_thread_indexed_tree_bench<Poseidon2>)
358+
BENCHMARK(multi_thread_indexed_tree_bench<Poseidon2, SEQUENTIAL>)
222359
->Unit(benchmark::kMillisecond)
223360
->RangeMultiplier(2)
224361
->Range(512, 8192)

barretenberg/cpp/src/barretenberg/crypto/merkle_tree/append_only_tree/content_addressed_append_only_tree.hpp

+10
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,16 @@ std::optional<fr> ContentAddressedAppendOnlyTree<Store, HashingPolicy>::find_lea
489489

490490
if (!child.has_value()) {
491491
// std::cout << "No child" << std::endl;
492+
// We still need to update the cache with the sibling. The fact that under us there is an empty subtree
493+
// doesn't mean that same is happening with our sibling.
494+
if (updateNodesByIndexCache) {
495+
child_index_at_level = is_right ? (child_index_at_level * 2) + 1 : (child_index_at_level * 2);
496+
std::optional<fr> sibling = is_right ? nodePayload.left : nodePayload.right;
497+
index_t sibling_index_at_level = is_right ? child_index_at_level - 1 : child_index_at_level + 1;
498+
if (sibling.has_value()) {
499+
store_->put_cached_node_by_index(i + 1, sibling_index_at_level, sibling.value(), false);
500+
}
501+
}
492502
return std::nullopt;
493503
}
494504
// std::cout << "Found child " << child.value() << std::endl;

0 commit comments

Comments
 (0)