parent
4dd6de1b0a
commit
751a442554
@ -0,0 +1,47 @@
|
||||
#include "../threading.h"
|
||||
#include <thread>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
using namespace std;
|
||||
|
||||
FILE *fp;
|
||||
int testing_throughput(uint32_t n_jobs){
|
||||
printf("Threadpool througput test with %u jobs.\n", n_jobs);
|
||||
|
||||
auto tp = ThreadPool(thread::hardware_concurrency());
|
||||
getchar();
|
||||
auto i = 0u;
|
||||
fp = fopen("tmp.tmp", "w");
|
||||
auto time = chrono::high_resolution_clock::now();
|
||||
while(i++ < n_jobs) tp.enqueue_task({ [](void* f) {fprintf(fp, "%d ", *(int*)f); free(f); }, new int(i) });
|
||||
puts("done dispatching.");
|
||||
while (tp.busy()) this_thread::sleep_for(1s);
|
||||
auto t = (chrono::high_resolution_clock::now() - time).count();
|
||||
printf("\nTr: %u, Ti: %lld \nThroughput: %lf transactions/ns\n", i, t, i/(double)(t));
|
||||
//this_thread::sleep_for(2s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int testing_transaction(uint32_t n_burst, uint32_t n_batch,
|
||||
uint32_t base_time, uint32_t var_time){
|
||||
printf("Threadpool transaction test: burst: %u, batch: %u, time: [%u, %u].\n"
|
||||
, n_burst, n_batch, base_time, var_time + base_time);
|
||||
|
||||
auto tp = ThreadPool(thread::hardware_concurrency());
|
||||
getchar();
|
||||
auto i = 0u, j = 0u;
|
||||
auto time = chrono::high_resolution_clock::now();
|
||||
while(j++ < n_batch){
|
||||
i = 0u;
|
||||
while(i++ < n_burst)
|
||||
tp.enqueue_task({ [](void* f) { printf( "%d ", *(int*)f); free(f); }, new int(i) });
|
||||
fflush(stdout);
|
||||
this_thread::sleep_for(chrono::microseconds(rand()%var_time + base_time));
|
||||
}
|
||||
puts("done dispatching.");
|
||||
while (tp.busy()) this_thread::sleep_for(1s);
|
||||
auto t = (chrono::high_resolution_clock::now() - time).count();
|
||||
printf("\nTr: %u, Ti: %lld \nThroughput: %lf transactions/ns\n", j*i, t, j*i/(double)(t));
|
||||
return 0;
|
||||
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
#include "threading.h"
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <deque>
|
||||
|
||||
using namespace std;
|
||||
using namespace chrono_literals;
|
||||
|
||||
#define A_TP_HAVE_PAYLOAD(x) ((x) & 0b1)
|
||||
#define A_TP_SET_PAYLOAD(x) ((x) |= 0b1)
|
||||
#define A_TP_UNSET_PAYLOAD(x) ((x) &= 0xfe)
|
||||
#define A_TP_IS_RUNNING(x) ((x) & 0b10)
|
||||
|
||||
void ThreadPool::daemon_proc(uint32_t id){
|
||||
auto tf = static_cast<atomic<uint8_t>*>(this->thread_flags);
|
||||
auto ticking = static_cast<atomic<bool>*>(this->ticking);
|
||||
|
||||
for(; tf[id]; this_thread::sleep_for(*ticking? 0ns:100ms)) {
|
||||
if (A_TP_HAVE_PAYLOAD(tf[id])) {
|
||||
A_TP_SET_PAYLOAD(tf[id]);
|
||||
current_payload[id]();
|
||||
current_payload[id].empty();
|
||||
A_TP_UNSET_PAYLOAD(tf[id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadPool::tick(){
|
||||
auto pq_lock = static_cast<mutex*>(payload_queue_lock);
|
||||
auto pq = static_cast<deque<payload_t> *>(payload_queue);
|
||||
auto tf = static_cast<atomic<uint8_t>*>(this->thread_flags);
|
||||
auto ticking = static_cast<atomic<bool>*>(this->ticking);
|
||||
auto th = static_cast<thread*>(this->thread_handles);
|
||||
for(; !this->terminate; this_thread::sleep_for(50ms)){
|
||||
if(*ticking) {
|
||||
bool quit = false;
|
||||
for(; !quit; ){
|
||||
for(uint32_t i = 0; i < n_threads; ++i){
|
||||
if(!A_TP_HAVE_PAYLOAD(tf[i])){
|
||||
pq_lock->lock();
|
||||
payload_t& p = pq->front();
|
||||
current_payload[i] = p;
|
||||
A_TP_SET_PAYLOAD(tf[i]);
|
||||
pq->pop_front();
|
||||
quit = !pq->size();
|
||||
pq_lock->unlock();
|
||||
if (quit) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
puts("done");
|
||||
*ticking = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < n_threads; ++i)
|
||||
tf[i] &= 0xfd;
|
||||
for (uint32_t i = 0; i < n_threads; ++i)
|
||||
th[i].join();
|
||||
|
||||
delete[] th;
|
||||
delete[] tf;
|
||||
delete pq;
|
||||
delete pq_lock;
|
||||
delete ticking;
|
||||
auto cp = static_cast<payload_t*>(current_payload);
|
||||
delete[] cp;
|
||||
}
|
||||
|
||||
ThreadPool::ThreadPool(uint32_t n_threads)
|
||||
: n_threads(n_threads) {
|
||||
printf("Thread pool started with %u threads;", n_threads);
|
||||
fflush(stdout);
|
||||
this->terminate = false;
|
||||
payload_queue = new deque<payload_t>;
|
||||
auto th = new thread[n_threads];
|
||||
auto tf = new atomic<uint8_t>[n_threads];
|
||||
|
||||
thread_handles = th;
|
||||
thread_flags = tf;
|
||||
ticking = static_cast<void*>(new atomic<bool>(false));
|
||||
|
||||
for (uint32_t i = 0; i < n_threads; ++i){
|
||||
atomic_init(tf + i, 0b10);
|
||||
th[i] = thread(&ThreadPool::daemon_proc, this, i);
|
||||
}
|
||||
|
||||
payload_queue_lock = new mutex();
|
||||
tick_handle = new thread(&ThreadPool::tick, this);
|
||||
current_payload = new payload_t[n_threads];
|
||||
}
|
||||
|
||||
|
||||
void ThreadPool::enqueue_task(const payload_t& payload){
|
||||
auto pq_lock = static_cast<mutex*>(payload_queue_lock);
|
||||
auto pq = static_cast<deque<payload_t> *>(payload_queue);
|
||||
auto tf = static_cast<atomic<uint8_t>*>(this->thread_flags);
|
||||
auto& ticking = *static_cast<atomic<bool>*>(this->ticking);
|
||||
|
||||
if (!ticking){
|
||||
for (uint32_t i = 0; i < n_threads; ++i){
|
||||
if(!A_TP_HAVE_PAYLOAD(tf[i])){
|
||||
current_payload[i] = payload;
|
||||
A_TP_SET_PAYLOAD(tf[i]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pq_lock->lock();
|
||||
pq->push_back(payload);
|
||||
ticking = true;
|
||||
pq_lock->unlock();
|
||||
}
|
||||
|
||||
ThreadPool::~ThreadPool() {
|
||||
this->terminate = true;
|
||||
auto tick = static_cast<thread*> (tick_handle);
|
||||
tick->join();
|
||||
delete tick;
|
||||
puts("Thread pool terminated.");
|
||||
}
|
||||
|
||||
bool ThreadPool::busy(){
|
||||
if (!*(atomic<bool>*)ticking) {
|
||||
for (int i = 0; i < n_threads; ++i)
|
||||
if (A_TP_HAVE_PAYLOAD(((atomic<uint8_t>*)thread_flags)[i]))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
#ifndef _AQ_THREADING_H
|
||||
#define _AQ_THREADING_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
class ThreadPool{
|
||||
|
||||
public:
|
||||
typedef void(*payload_fn_t)(void*);
|
||||
|
||||
struct payload_t{
|
||||
payload_fn_t f;
|
||||
void* args;
|
||||
constexpr payload_t(payload_fn_t f, void* args) noexcept
|
||||
: f(f), args(args) {}
|
||||
constexpr payload_t() noexcept
|
||||
: f(nullptr), args(nullptr) {};
|
||||
bool is_empty() const { return f && args; }
|
||||
void empty() { f = nullptr; args = nullptr; }
|
||||
void operator()() { f(args); }
|
||||
};
|
||||
ThreadPool(uint32_t n_threads = 0);
|
||||
void enqueue_task(const payload_t& payload);
|
||||
bool busy();
|
||||
virtual ~ThreadPool();
|
||||
|
||||
private:
|
||||
uint32_t n_threads;
|
||||
void* thread_handles;
|
||||
void* thread_flags;
|
||||
payload_t* current_payload;
|
||||
void* payload_queue;
|
||||
void* tick_handle;
|
||||
void* ticking;
|
||||
void* payload_queue_lock;
|
||||
bool terminate;
|
||||
void tick();
|
||||
void daemon_proc(uint32_t);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in new issue