Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
trace_container.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <ranges>
5
11
12namespace bb::avm2::tracegen {
13namespace {
14
15// We need a zero value to return (a reference to) when a value is not found.
16static const FF zero = FF::zero();
17
18} // namespace
19
23
24const FF& TraceContainer::get(Column col, uint32_t row) const
25{
26 auto& column_data = (*trace)[static_cast<size_t>(col)];
27 const size_t shard_idx = row / INTERVAL_SIZE;
28 if (shard_idx >= NUM_SHARDS) {
29 return zero;
30 }
32 if (shard == nullptr) {
33 return zero;
34 }
35 return shard->rows[row % INTERVAL_SIZE];
36}
37
39{
40 if (is_shift(col)) {
41 return get(unshift_column(col).value(), row + 1);
42 }
43 return get(static_cast<Column>(col), row);
44}
45
47{
49 if (shard != nullptr) {
50 return *shard;
51 }
52 // Slow path: this slot has never been written. Construct a shard and install it with a single CAS.
53 // Creators of different shards target different atomics and run fully in parallel with no lock. If we
54 // lose the race for this slot (only possible when two chunks share a shard at a boundary), we discard
55 // our spare copy and use the winner's.
57 ColumnInterval* expected = nullptr;
58 if (column_data.slots[shard_idx].compare_exchange_strong(
60 return *fresh.release();
61 }
62 return *expected; // CAS failure loaded the winning pointer into `expected` (acquire).
63}
64
65void TraceContainer::set(Column col, uint32_t row, const FF& value)
66{
67 auto& column_data = (*trace)[static_cast<size_t>(col)];
68 const size_t shard_idx = row / INTERVAL_SIZE;
69 BB_ASSERT_LT(shard_idx, NUM_SHARDS, "row exceeds the maximum trace size");
70 const uint32_t offset = row % INTERVAL_SIZE;
71
72 if (!value.is_zero()) {
73 // Lock-free: a single atomic load finds the shard (created on first write), then we write our
74 // own dense cell directly. Different rows are distinct array elements, so concurrent writers of
75 // this column (or even of the same shard, at a chunk boundary) never race and never serialize.
77 } else {
78 // Zero value: clear if present. We never create a shard (clearing an absent row is a no-op).
80 if (shard != nullptr) {
82 }
83 }
84}
85
86void TraceContainer::set(uint32_t row, std::span<const std::pair<Column, FF>> values)
87{
88 for (const auto& [col, value] : values) {
89 set(col, row, value);
90 }
91}
92
94{
95 if (size == 0) {
96 return;
97 }
98 auto& column_data = (*trace)[static_cast<size_t>(col)];
99 const size_t num_shards = std::min((size + INTERVAL_SIZE - 1) / INTERVAL_SIZE, NUM_SHARDS);
100 // Each shard's dense row array is full size on creation, so reserving just materializes the shards up
101 // front (e.g. for precomputed columns). Lock-free: get_or_create_shard installs each via CAS.
102 for (size_t k = 0; k < num_shards; ++k) {
104 }
105}
106
108{
109 // The number of rows is (highest non-zero absolute row + 1). We find it by scanning shards from the
110 // top down and, within the first non-empty shard, scanning its rows from the top. Shards are
111 // top-dense, so this terminates almost immediately. This is only called after the parallel fill
112 // phase, so no lock is needed. Lower shards cannot hold a higher row, so the first hit is the answer.
113 auto& column_data = (*trace)[static_cast<size_t>(col)];
114 for (size_t k = NUM_SHARDS; k-- > 0;) {
116 if (shard_ptr == nullptr) {
117 continue;
118 }
119 const auto& rows = shard_ptr->rows;
120 const uint32_t base = static_cast<uint32_t>(k) * INTERVAL_SIZE;
121 for (uint32_t off = INTERVAL_SIZE; off-- > 0;) {
122 if (!rows[off].is_zero()) {
123 return base + off + 1;
124 }
125 }
126 }
127 return 0;
128}
129
131{
132 uint32_t max_rows = 0;
133 for (size_t col = WITNESS_START_IDX; col < WITNESS_END_IDX; ++col) {
135 }
136 return max_rows;
137}
138
140{
141 uint32_t max_rows = 0;
142 for (size_t col = 0; col < num_columns(); ++col) {
144 }
145 return max_rows;
146}
147
148void TraceContainer::visit_column(Column col, const std::function<void(uint32_t, const FF&)>& visitor) const
149{
150 auto& column_data = (*trace)[static_cast<size_t>(col)];
151 for (size_t k = 0; k < NUM_SHARDS; ++k) {
153 if (shard_ptr == nullptr) {
154 continue;
155 }
156 auto& shard = *shard_ptr;
157 const uint32_t base = static_cast<uint32_t>(k) * INTERVAL_SIZE;
158 for (uint32_t off = 0; off < INTERVAL_SIZE; ++off) {
159 if (!shard.rows[off].is_zero()) {
160 visitor(base + off, shard.rows[off]);
161 }
162 }
163 }
164}
165
167{
168 for (const auto& col : cols) {
170 }
171}
172
174{
176 auto& column_data = (*trace)[static_cast<size_t>(col)];
177 for (size_t k = 0; k < NUM_SHARDS; ++k) {
179 if (shard_ptr == nullptr) {
180 continue;
181 }
182 auto& shard = *shard_ptr;
183 for (auto& value : shard.rows) {
184 if (!value.is_zero()) {
185 ff_vector.push_back(value);
186 }
187 }
188 }
189 FF::batch_invert<RefVector<FF>>(ff_vector);
190}
191
193{
194 // Lock-free: exchange hands each non-null shard pointer to exactly one caller, which frees it.
195 auto& column_data = (*trace)[static_cast<size_t>(col)];
196 for (size_t k = 0; k < NUM_SHARDS; ++k) {
197 delete column_data.slots[k].exchange(nullptr, std::memory_order_acq_rel);
198 }
199}
200
201} // namespace bb::avm2::tracegen
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:143
A template class for a reference vector. Behaves as if std::vector<T&> was possible.
static constexpr size_t num_columns()
const FF & get(Column col, uint32_t row) const
void reserve_column(Column col, size_t size)
const FF & get_column_or_shift(ColumnAndShifts col, uint32_t row) const
void invert_columns(std::span< const Column > cols)
static ColumnInterval & get_or_create_shard(SparseColumn &column_data, size_t shard_idx)
void visit_column(Column col, const std::function< void(uint32_t, const FF &)> &visitor) const
uint32_t get_column_rows(Column col) const
void set(Column col, uint32_t row, const FF &value)
static constexpr uint32_t INTERVAL_SIZE
TestTraceContainer trace
ssize_t offset
Definition engine.cpp:62
bool is_shift(ColumnAndShifts c)
std::optional< Column > unshift_column(ColumnAndShifts c)
constexpr auto WITNESS_END_IDX
Definition columns.hpp:73
AvmFlavorSettings::FF FF
Definition field.hpp:10
constexpr auto WITNESS_START_IDX
Definition columns.hpp:72
constexpr auto NUM_COLUMNS_WITHOUT_SHIFTS
Definition columns.hpp:40
ColumnAndShifts
Definition columns.hpp:34
STL namespace.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
BB_INLINE constexpr bool is_zero() const noexcept