2018-03-22 13:20:58 +11:00
|
|
|
#include "parallel/queue.hpp"
|
2018-03-21 18:53:24 +11:00
|
|
|
#include "job/flag.hpp"
|
2018-03-22 13:02:51 +11:00
|
|
|
#include "job/semaphore.hpp"
|
2018-03-21 18:53:24 +11:00
|
|
|
#include "debug.hpp"
|
2018-03-22 13:02:51 +11:00
|
|
|
#include "tap.hpp"
|
2018-03-21 18:53:24 +11:00
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <thread>
|
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
|
|
|
|
static constexpr uint32_t slots = 4;
|
2018-03-22 13:02:51 +11:00
|
|
|
static constexpr int parallelism = 8;
|
|
|
|
static constexpr int chunk_size = 1<<12;
|
2018-03-21 18:53:24 +11:00
|
|
|
|
|
|
|
util::job::flag start;
|
|
|
|
|
2018-03-22 13:20:58 +11:00
|
|
|
using queue_t = util::parallel::queue<int,slots>;
|
2018-03-21 18:53:24 +11:00
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2018-03-22 13:02:51 +11:00
|
|
|
// push numbers in the range [first,first+chunk_size) to the queue
|
|
|
|
void
|
|
|
|
produce (queue_t &dst, const int first)
|
|
|
|
{
|
|
|
|
start.wait ();
|
|
|
|
|
|
|
|
for (int i = first; i < first + chunk_size; ++i)
|
|
|
|
while (!dst.push (i))
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// pop chunk_size elements from the queue to a vector
|
|
|
|
void
|
|
|
|
consume (queue_t &src, util::view<int*> dst)
|
2018-03-21 18:53:24 +11:00
|
|
|
{
|
|
|
|
(void)dst;
|
|
|
|
start.wait ();
|
|
|
|
|
|
|
|
int last;
|
|
|
|
|
|
|
|
for (int i = 0; i < chunk_size; ++i) {
|
|
|
|
while (!src.pop (last))
|
|
|
|
;
|
|
|
|
dst[i] = last;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2018-03-22 13:02:51 +11:00
|
|
|
// * create n producers which push the c integers over [n*c,n*(c+1)) into a queue
|
|
|
|
// * create n consumers which pop c integers from a queue into a store
|
|
|
|
// * ensure all integers we expected are in the store
|
|
|
|
//
|
|
|
|
// we use an artificially small queue size to force contention.
|
|
|
|
//
|
|
|
|
// to stress test we increase the threads, increase the data length, and
|
|
|
|
// decrease the queue size.
|
2018-03-21 18:53:24 +11:00
|
|
|
int
|
|
|
|
main ()
|
|
|
|
{
|
2018-03-22 13:02:51 +11:00
|
|
|
util::TAP::logger tap;
|
2018-03-21 18:53:24 +11:00
|
|
|
queue_t src;
|
|
|
|
|
2018-03-22 13:02:51 +11:00
|
|
|
// start n consumers, and n producers that fill an array with integers
|
|
|
|
std::vector<int> dst (parallelism * chunk_size);
|
|
|
|
std::vector<std::thread> consumers;
|
|
|
|
std::vector<std::thread> producers;
|
2018-03-21 18:53:24 +11:00
|
|
|
|
|
|
|
for (int i = 0; i < parallelism; ++i) {
|
2018-03-22 13:02:51 +11:00
|
|
|
consumers.emplace_back (
|
|
|
|
consume,
|
|
|
|
std::ref (src),
|
|
|
|
util::view{
|
|
|
|
dst.data() + i * chunk_size,
|
|
|
|
chunk_size
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
producers.emplace_back (produce, std::ref (src), i * chunk_size);
|
2018-03-21 18:53:24 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
start.notify ();
|
|
|
|
|
2018-03-22 13:02:51 +11:00
|
|
|
// wait for everyone to complete
|
|
|
|
for (auto &t: producers)
|
|
|
|
t.join ();
|
|
|
|
for (auto &t: consumers)
|
2018-03-21 18:53:24 +11:00
|
|
|
t.join ();
|
|
|
|
|
2018-03-22 13:02:51 +11:00
|
|
|
// sort the result and ensure all values are present
|
|
|
|
std::sort (dst.begin (), dst.end ());
|
2018-03-21 18:53:24 +11:00
|
|
|
|
|
|
|
int missing = 0;
|
|
|
|
for (int i = 0; i < parallelism * chunk_size; ++i)
|
2018-03-22 13:02:51 +11:00
|
|
|
if (dst[i] != i)
|
2018-03-21 18:53:24 +11:00
|
|
|
++missing;
|
|
|
|
|
2018-03-22 13:02:51 +11:00
|
|
|
tap.expect_eq (missing, 0, "no lost values in parallel produce/consume queue");
|
|
|
|
return tap.status ();
|
2018-03-21 18:53:24 +11:00
|
|
|
}
|