mirror of
https://github.com/Z3Prover/z3
synced 2025-06-06 14:13:23 +00:00
thread pool for scoped_timer (#4748)
creating a fresh thread for every scoped_timer has significant overhead in some use cases. this patch creates a persistent pool of worker threads to do this job, resulting in 20-30% speedup of some alive2 jobs on a large multicore
This commit is contained in:
parent
0c354c7aab
commit
a95c35dadb
1 changed files with 59 additions and 14 deletions
|
@ -23,37 +23,82 @@ Revision History:
|
||||||
#include "util/util.h"
|
#include "util/util.h"
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
|
#include <condition_variable>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct state {
|
||||||
struct scoped_timer::imp {
|
std::thread * m_thread = nullptr;
|
||||||
private:
|
|
||||||
std::thread m_thread;
|
|
||||||
std::timed_mutex m_mutex;
|
std::timed_mutex m_mutex;
|
||||||
|
unsigned ms;
|
||||||
|
event_handler * eh;
|
||||||
|
int work = 0;
|
||||||
|
std::condition_variable_any cv;
|
||||||
|
};
|
||||||
|
|
||||||
static void thread_func(unsigned ms, event_handler * eh, std::timed_mutex * mutex) {
|
/*
|
||||||
auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms);
|
* NOTE: this implementation deliberately leaks threads when Z3
|
||||||
|
* exits. this is preferable to deallocating on exit, because
|
||||||
|
* destructing threads blocked on condition variables leads to
|
||||||
|
* deadlock.
|
||||||
|
*/
|
||||||
|
static std::vector<state *> available_workers;
|
||||||
|
static std::mutex workers;
|
||||||
|
|
||||||
while (!mutex->try_lock_until(end)) {
|
static void thread_func(state *s) {
|
||||||
|
workers.lock();
|
||||||
|
while (true) {
|
||||||
|
s->cv.wait(workers, [=]{ return s->work > 0; });
|
||||||
|
workers.unlock();
|
||||||
|
|
||||||
|
auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(s->ms);
|
||||||
|
|
||||||
|
while (!s->m_mutex.try_lock_until(end)) {
|
||||||
if (std::chrono::steady_clock::now() >= end) {
|
if (std::chrono::steady_clock::now() >= end) {
|
||||||
eh->operator()(TIMEOUT_EH_CALLER);
|
s->eh->operator()(TIMEOUT_EH_CALLER);
|
||||||
return;
|
goto next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex->unlock();
|
s->m_mutex.unlock();
|
||||||
|
|
||||||
|
next:
|
||||||
|
s->work = 0;
|
||||||
|
workers.lock();
|
||||||
|
available_workers.push_back(s);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct scoped_timer::imp {
|
||||||
|
private:
|
||||||
|
state *s;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
imp(unsigned ms, event_handler * eh) {
|
imp(unsigned ms, event_handler * eh) {
|
||||||
m_mutex.lock();
|
workers.lock();
|
||||||
m_thread = std::thread(thread_func, ms, eh, &m_mutex);
|
if (available_workers.empty()) {
|
||||||
|
workers.unlock();
|
||||||
|
s = new state;
|
||||||
|
} else {
|
||||||
|
s = available_workers.back();
|
||||||
|
available_workers.pop_back();
|
||||||
|
workers.unlock();
|
||||||
|
}
|
||||||
|
s->ms = ms;
|
||||||
|
s->eh = eh;
|
||||||
|
s->m_mutex.lock();
|
||||||
|
s->work = 1;
|
||||||
|
if (!s->m_thread) {
|
||||||
|
s->m_thread = new std::thread(thread_func, s);
|
||||||
|
s->m_thread->detach();
|
||||||
|
} else {
|
||||||
|
s->cv.notify_one();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
~imp() {
|
~imp() {
|
||||||
m_mutex.unlock();
|
s->m_mutex.unlock();
|
||||||
m_thread.join();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue