Skip to content

Commit 509060b

Browse files
committed
Rework alignment in consumer_queue
alignas(64) wasn't supported at all for dynamically allocated memory (<C++17) Instead, insert padding between fields that shouldn't share a cacheline.
1 parent 0035de7 commit 509060b

File tree

2 files changed

+36
-15
lines changed

2 files changed

+36
-15
lines changed

src/consumer_queue.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,14 @@
88
using namespace lsl;
99

1010
consumer_queue::consumer_queue(std::size_t size, send_buffer_p registry)
11-
: registry_(std::move(registry)), buffer_(new item_t[size]), size_(size),
11+
: buffer_(new item_t[size]), size_(size),
1212
// largest integer at which we can wrap correctly
1313
wrap_at_(std::numeric_limits<std::size_t>::max() - size -
14-
std::numeric_limits<std::size_t>::max() % size) {
14+
std::numeric_limits<std::size_t>::max() % size),
15+
registry_(std::move(registry)) {
1516
assert(size_ > 1);
1617
for (std::size_t i = 0; i < size_; ++i)
1718
buffer_[i].seq_state.store(i, std::memory_order_release);
18-
write_idx_.store(0, std::memory_order_release);
19-
read_idx_.store(0, std::memory_order_release);
20-
done_sync_.store(false, std::memory_order_release);
2119
if (registry_) registry_->register_consumer(this);
2220
}
2321

src/consumer_queue.h

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@ constexpr int CACHELINE_BYTES = 128;
1919
constexpr int CACHELINE_BYTES = 64;
2020
#endif
2121

22+
constexpr int _min(int a, int b) { return a > b ? b : a; }
23+
24+
/// calculate the needed padding to separate two members E1 and E2 by at least CACHELINE_BYTES
25+
template <typename E1, typename E2, typename... T> constexpr std::size_t padding() {
26+
return CACHELINE_BYTES -
27+
_min(sizeof(std::tuple<E1, T..., E2>) - sizeof(E1) - sizeof(E2), CACHELINE_BYTES - 1);
28+
}
29+
30+
template <typename E1, typename E2, typename... T> struct Padding {
31+
const char pad[padding<E1, E2, T...>()]{0};
32+
};
33+
2234
/**
2335
* A thread-safe producer/consumer queue of unread samples.
2436
*
@@ -159,24 +171,35 @@ class consumer_queue {
159171
return ++x == wrap_at_ ? 0 : x;
160172
}
161173

162-
/// optional consumer registry
163-
send_buffer_p registry_;
174+
/// current read position
175+
std::atomic<std::size_t> read_idx_{0};
176+
/// condition for waiting with timeout
177+
std::condition_variable cv_;
164178
/// the sample buffer
165179
item_t *buffer_;
180+
181+
/// padding to ensure read_idx_ and write_idx_ don't share a cacheline
182+
Padding<std::size_t, std::size_t, std::condition_variable, void *> pad;
183+
184+
/// current write position
185+
std::atomic<std::size_t> write_idx_{0};
166186
/// max number of elements in the queue
167187
const std::size_t size_;
168188
/// threshold at which to wrap read/write indices
169189
const std::size_t wrap_at_;
170-
// whether we have performed a sync on the data stored by the constructor
171-
alignas(CACHELINE_BYTES) std::atomic<bool> done_sync_;
172-
/// current write position
173-
alignas(CACHELINE_BYTES) std::atomic<std::size_t> write_idx_;
174-
/// current read position
175-
alignas(CACHELINE_BYTES) std::atomic<std::size_t> read_idx_;
176190
/// for use with the condition variable
177191
std::mutex mut_;
178-
/// condition for waiting with timeout
179-
std::condition_variable cv_;
192+
193+
/// optional consumer registry
194+
send_buffer_p registry_;
195+
196+
/// padding to ensure write_ix_ and done_sync_ don't share a cacheline
197+
#if UINTPTR_MAX <= 0xFFFFFFFF
198+
Padding<std::size_t, bool, std::size_t, std::size_t, std::mutex, send_buffer_p> pad2;
199+
#endif
200+
201+
/// whether we have performed a sync on the data stored by the constructor
202+
std::atomic<bool> done_sync_{false};
180203
};
181204

182205
} // namespace lsl

0 commit comments

Comments
 (0)