Skip to content

Commit 68b7a6f

Browse files
Coldwingslihuiba
authored andcommitted
Adjust MPMCRingQueue memory layout for better performance
1 parent 2ebecb4 commit 68b7a6f

2 files changed

Lines changed: 38 additions & 22 deletions

File tree

common/lockfree_queue.h

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,12 @@ class LockfreeMPMCRingQueue : public LockfreeRingQueueBase<T, N> {
169169
using Base::idx;
170170
using Base::tail;
171171

172-
std::atomic<uint64_t> marks[Base::capacity]{};
173-
T slots[Base::capacity];
172+
struct alignas(Base::CACHELINE_SIZE) packedslot {
173+
T data;
174+
std::atomic<uint64_t> mark{0};
175+
};
176+
177+
packedslot slots[Base::capacity];
174178

175179
uint64_t this_turn_write(const uint64_t x) const {
176180
return (Base::turn(x) << 1) + 1;
@@ -191,8 +195,9 @@ class LockfreeMPMCRingQueue : public LockfreeRingQueueBase<T, N> {
191195
bool push(const T& x) {
192196
auto t = tail.load(std::memory_order_acquire);
193197
for (;;) {
194-
auto& slot = slots[idx(t)];
195-
auto& mark = marks[idx(t)];
198+
auto& ps = slots[idx(t)];
199+
auto& slot = ps.data;
200+
auto& mark = ps.mark;
196201
if (mark.load(std::memory_order_acquire) == last_turn_read(t)) {
197202
if (tail.compare_exchange_strong(t, t + 1)) {
198203
slot = x;
@@ -213,8 +218,9 @@ class LockfreeMPMCRingQueue : public LockfreeRingQueueBase<T, N> {
213218
bool pop(T& x) {
214219
auto h = head.load(std::memory_order_acquire);
215220
for (;;) {
216-
auto& slot = slots[idx(h)];
217-
auto& mark = marks[idx(h)];
221+
auto& ps = slots[idx(h)];
222+
auto& slot = ps.data;
223+
auto& mark = ps.mark;
218224
if (mark.load(std::memory_order_acquire) == this_turn_write(h)) {
219225
if (head.compare_exchange_strong(h, h + 1)) {
220226
x = slot;
@@ -237,8 +243,9 @@ class LockfreeMPMCRingQueue : public LockfreeRingQueueBase<T, N> {
237243
static_assert(std::is_base_of<PauseBase, Pause>::value,
238244
"Pause should be derived by PauseBase");
239245
auto const t = tail.fetch_add(1);
240-
auto& slot = slots[idx(t)];
241-
auto& mark = marks[idx(t)];
246+
auto& ps = slots[idx(t)];
247+
auto& slot = ps.data;
248+
auto& mark = ps.mark;
242249
while (mark.load(std::memory_order_acquire) != last_turn_read(t))
243250
Pause::pause();
244251
slot = x;
@@ -250,8 +257,9 @@ class LockfreeMPMCRingQueue : public LockfreeRingQueueBase<T, N> {
250257
static_assert(std::is_base_of<PauseBase, Pause>::value,
251258
"Pause should be derived by PauseBase");
252259
auto const h = head.fetch_add(1);
253-
auto& slot = slots[idx(h)];
254-
auto& mark = marks[idx(h)];
260+
auto& ps = slots[idx(h)];
261+
auto& slot = ps.data;
262+
auto& mark = ps.mark;
255263
while (mark.load(std::memory_order_acquire) != this_turn_write(h))
256264
Pause::pause();
257265
T ret = slot;

common/test/test_lockfree.cpp

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ limitations under the License.
3434
#include <thread>
3535
#include <vector>
3636
#include <photon/common/alog.h>
37+
#include <gtest/gtest.h>
38+
#include <gflags/gflags.h>
3739
#include "../../test/ci-tools.h"
3840

3941
static constexpr size_t sender_num = 4;
@@ -86,7 +88,7 @@ int test_queue(const char *name, QType &queue) {
8688
auto begin = std::chrono::steady_clock::now();
8789
for (size_t i = 0; i < receiver_num; i++) {
8890
receivers.emplace_back([i, &queue] {
89-
photon::set_cpu_affinity(i);
91+
photon::set_cpu_affinity(i * 2);
9092
std::chrono::nanoseconds rspent(std::chrono::nanoseconds(0));
9193
for (size_t x = 0; x < items_num / receiver_num; x++) {
9294
int t;
@@ -109,7 +111,7 @@ int test_queue(const char *name, QType &queue) {
109111
}
110112
for (size_t i = 0; i < sender_num; i++) {
111113
senders.emplace_back([i, &queue] {
112-
photon::set_cpu_affinity(i);
114+
photon::set_cpu_affinity(i * 2 + 1);
113115
std::chrono::nanoseconds wspent{std::chrono::nanoseconds(0)};
114116
for (size_t x = 0; x < items_num / sender_num; x++) {
115117
auto tm = std::chrono::high_resolution_clock::now();
@@ -135,9 +137,8 @@ int test_queue(const char *name, QType &queue) {
135137
LOG_DEBUG("` ` p ` c, ` items, Spent ` us", name, sender_num, receiver_num, items_num,
136138
std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count());
137139
for (size_t i = 0; i < items_num / sender_num; i++) {
138-
if (sc[i] != rc[i] || sc[i] != sender_num) {
139-
LOG_DEBUG("MISMATCH ` ` `", i, sc[i].load(), rc[i].load());
140-
}
140+
EXPECT_EQ(sc[i], rc[i]);
141+
EXPECT_EQ(sc[i].load(), (int)sender_num);
141142
sc[i] = 0;
142143
rc[i] = 0;
143144
}
@@ -153,7 +154,7 @@ int test_queue_batch(const char *name, QType &queue) {
153154
auto begin = std::chrono::steady_clock::now();
154155
for (size_t i = 0; i < receiver_num; i++) {
155156
receivers.emplace_back([i, &queue] {
156-
photon::set_cpu_affinity(i);
157+
photon::set_cpu_affinity(i * 2);
157158
int buffer[32];
158159
size_t size;
159160
int amount = items_num / receiver_num;
@@ -181,7 +182,7 @@ int test_queue_batch(const char *name, QType &queue) {
181182
}
182183
for (size_t i = 0; i < sender_num; i++) {
183184
senders.emplace_back([i, &queue] {
184-
photon::set_cpu_affinity(i);
185+
photon::set_cpu_affinity(i * 2 + 1);
185186
std::vector<int> vec;
186187
vec.resize(items_num / sender_num);
187188
for (size_t x = 0; x < items_num / sender_num; x++) {
@@ -215,26 +216,33 @@ int test_queue_batch(const char *name, QType &queue) {
215216
LOG_DEBUG("` ` p ` c, ` items, Spent ` us", name, sender_num, receiver_num, items_num,
216217
std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count());
217218
for (size_t i = 0; i < items_num / sender_num; i++) {
218-
if (sc[i] != rc[i] || sc[i] != sender_num) {
219-
LOG_DEBUG("MISMATCH ` ` `", i, sc[i].load(), rc[i].load());
220-
}
219+
EXPECT_EQ(sc[i], rc[i]);
220+
EXPECT_EQ(sc[i].load(), (int)sender_num);
221221
sc[i] = 0;
222222
rc[i] = 0;
223223
}
224224
std::this_thread::sleep_for(std::chrono::seconds(1));
225225
return 0;
226226
}
227227

228-
int main() {
228+
TEST(lockfree_queue, test_queue) {
229229
#ifdef TESTING_ENABLE_BOOST
230230
test_queue<NoLock, NoLock>("BoostQueue", bqueue);
231231
#endif
232232
test_queue<NoLock, NoLock>("PhotonLockfreeMPMCQueue", lqueue);
233233
test_queue<NoLock, NoLock>("PhotonLockfreeBatchMPMCQueue", lbqueue);
234-
test_queue_batch<NoLock, NoLock>("PhotonLockfreeBatchMPMCQueue+Batch", lbqueue);
234+
test_queue_batch<NoLock, NoLock>("PhotonLockfreeBatchMPMCQueue+Batch",
235+
lbqueue);
235236
#ifdef TESTING_ENABLE_BOOST
236237
test_queue<WithLock, WithLock>("BoostSPSCQueue", squeue);
237238
#endif
238239
test_queue<WithLock, WithLock>("PhotonSPSCQueue", cqueue);
239240
test_queue_batch<WithLock, WithLock>("PhotonSPSCQueue+Batch", cqueue);
240241
}
242+
243+
int main(int argc, char **arg) {
244+
if (!photon::is_using_default_engine()) return 0;
245+
::testing::InitGoogleTest(&argc, arg);
246+
gflags::ParseCommandLineFlags(&argc, &arg, true);
247+
return RUN_ALL_TESTS();
248+
}

0 commit comments

Comments
 (0)