add test_prod_cons function template; fix some bugs for multi-producers

This commit is contained in:
mutouyun 2018-11-26 11:56:59 +08:00
parent 7e24273a64
commit 0222af2221
2 changed files with 90 additions and 53 deletions

View File

@ -107,8 +107,7 @@ public:
do {
uc_t expected = 0;
if (el->head_.rf_.compare_exchange_weak(
expected, static_cast<uc_t>(conn_count()),
std::memory_order_consume, std::memory_order_relaxed)) {
expected, static_cast<uc_t>(conn_count()), std::memory_order_acq_rel)) {
break;
}
} while(1);
@ -117,38 +116,41 @@ public:
void commit(void* ptr) {
auto el = elem(ptr); // get the commit element
ui_t cm = index_of(el); // get the index of this element
ui_t wi = index_of(el); // get the index of this element (the write index)
do {
bool no_next_check;
uc_t curr;
bool no_next, cas;
uc_t curr = cr_.load(std::memory_order_relaxed), next;
do {
curr = cr_.load(std::memory_order_relaxed);
no_next_check = (index_of(curr) != cm);
if (no_next_check) {
next = curr;
if ((no_next = (index_of(curr) != wi)) /* assign & judge */) {
/*
* commit is not the current commit
* set wf_ for the other producer thread which is commiting
* the element matches cr_ could see it has commited
*/
el->head_.wf_.store(1, std::memory_order_relaxed);
el->head_.wf_.store(1, std::memory_order_release);
}
else {
/*
* no thread changes the cr_ except current thread at here
* so we could just fetch_add & break, no need to check cr_ again
* commit is the current commit
* so we should increase the cursor & go check the next
*/
cr_.fetch_add(1, std::memory_order_relaxed);
++next;
el->head_.wf_.store(0, std::memory_order_release);
no_next_check = false;
break;
}
/*
* it needs to go back and judge again
* when cr_ has been changed by the other producer thread
*/
} while(curr != cr_.load(std::memory_order_acq_rel));
} while(!(cas = cr_.compare_exchange_weak(curr, next, std::memory_order_acq_rel)) && no_next);
/*
* if compare_exchange failed & !no_next,
* means there is another producer thread just update this commit,
* so in this case we could just return
*/
if (no_next || (!cas/* && !no_next*/)) return;
// check next element has commited or not
if (no_next_check) return;
} while(el = elem(++cm), el->head_.wf_.load(std::memory_order_consume));
} while(el = elem(++wi), el->head_.wf_.load(std::memory_order_consume));
}
uc_t cursor(void) const {

View File

@ -4,6 +4,7 @@
#include <memory>
#include <new>
#include <vector>
#include <unordered_map>
#include <thread>
#include "circ_elem_array.h"
@ -17,7 +18,9 @@ class Unit : public TestSuite {
private slots:
void test_inst(void);
void test_prod_cons_1v1(void);
void test_prod_cons_1vN(void);
void test_prod_cons_Nv1(void);
} unit__;
#include "test_circ_elem_array.moc"
@ -38,51 +41,61 @@ void Unit::test_inst(void) {
cq__ = new cq_t;
std::cout << "sizeof(ipc::circ::elem_array<4096>) = " << sizeof(*cq__) << std::endl;
auto a = cq__->take(1);
auto b = cq__->take(2);
QCOMPARE(static_cast<std::size_t>(static_cast<ipc::byte_t*>(b) -
static_cast<ipc::byte_t*>(a)),
static_cast<std::size_t>(cq_t::elem_size));
// auto a = cq__->take(1);
// auto b = cq__->take(2);
// QCOMPARE(static_cast<std::size_t>(static_cast<ipc::byte_t*>(b) -
// static_cast<ipc::byte_t*>(a)),
// static_cast<std::size_t>(cq_t::elem_size));
}
void Unit::test_prod_cons_1vN(void) {
template <int N, int M, int Loops = 1000000>
void test_prod_cons(void) {
::new (cq__) cq_t;
std::thread consumers[3];
std::thread producers[N];
std::thread consumers[M];
std::atomic_int fini { 0 };
capo::stopwatch<> sw;
constexpr static int loops = 10000000;
for (auto& c : consumers) {
c = std::thread{[&] {
struct msg_t {
int pid_;
int dat_;
};
for (auto& t : consumers) {
t = std::thread{[&] {
auto cur = cq__->cursor();
std::cout << "start consumer " << &c << ": cur = " << (int)cur << std::endl;
std::cout << "start consumer " << &t << ": cur = " << (int)cur << std::endl;
cq__->connect();
auto disconn = [](cq_t* cq) { cq->disconnect(); };
std::unique_ptr<cq_t, decltype(disconn)> guard(cq__, disconn);
std::unique_ptr<cq_t, void(*)(cq_t*)> guard(cq__, [](cq_t* cq) { cq->disconnect(); });
std::vector<int> list;
int i = 0;
std::unordered_map<int, std::vector<int>> list;
do {
while (cur != cq__->cursor()) {
auto p = static_cast<int*>(cq__->take(cur));
int d = *p;
cq__->put(p);
if (d < 0) goto finished;
msg_t* pmsg = static_cast<msg_t*>(cq__->take(cur)),
msg = *pmsg;
cq__->put(pmsg);
if (msg.pid_ < 0) goto finished;
++cur;
list.push_back(d);
list[msg.pid_].push_back(msg.dat_);
}
} while(1);
finished:
if (++fini == std::extent<decltype(consumers)>::value) {
auto ts = sw.elapsed<std::chrono::microseconds>();
std::cout << "performance: " << (double(ts) / double(loops)) << " us/d" << std::endl;
std::cout << "[" << N << ":" << M << ", " << Loops << "]" << std::endl
<< "performance: " << (double(ts) / double(Loops)) << " us/d" << std::endl;
}
std::cout << "confirming..." << std::endl;
for (int d : list) {
for (int n = 0; n < static_cast<int>(std::extent<decltype(producers)>::value); ++n) {
auto& vec = list[n];
QCOMPARE(vec.size(), static_cast<std::size_t>(Loops));
int i = 0;
for (int d : vec) {
QCOMPARE(i, d);
++i;
}
}
}};
}
@ -90,20 +103,42 @@ void Unit::test_prod_cons_1vN(void) {
std::this_thread::yield();
}
std::cout << "start producer..." << std::endl;
std::cout << "start producers..." << std::endl;
std::atomic_flag started = ATOMIC_FLAG_INIT;
int pid = 0;
for (auto& t : producers) {
t = std::thread{[&, pid] {
if (!started.test_and_set()) {
sw.start();
for (int i = 0; i < loops; ++i) {
auto d = static_cast<int*>(cq__->acquire());
*d = i;
cq__->commit(d);
}
auto d = static_cast<int*>(cq__->acquire());
*d = -1;
cq__->commit(d);
for (int i = 0; i < Loops; ++i) {
msg_t* pmsg = static_cast<msg_t*>(cq__->acquire());
pmsg->pid_ = pid;
pmsg->dat_ = i;
cq__->commit(pmsg);
}
}};
++pid;
}
for (auto& t : producers) t.join();
// quit
msg_t* pmsg = static_cast<msg_t*>(cq__->acquire());
pmsg->pid_ = pmsg->dat_ = -1;
cq__->commit(pmsg);
for (auto& c : consumers) {
c.join();
}
for (auto& t : consumers) t.join();
}
void Unit::test_prod_cons_1v1(void) {
test_prod_cons<1, 1>();
}
void Unit::test_prod_cons_1vN(void) {
test_prod_cons<1, 3>();
}
void Unit::test_prod_cons_Nv1(void) {
test_prod_cons<2, 1>();
}
} // internal-linkage