Add thread pool class
Added a thread pool class loosely based on google3 code.
Modified the compiler to have a single thread pool instead of creating new threads in ForAll.
Moved barrier to be in top level directory as it is not GC specific code.
Performance Timings:
Reference:
boot.oat: 14.306596s
time mm oat-target:
real 2m33.748s
user 10m23.190s
sys 5m54.140s
Thread pool:
boot.oat: 13.111049s
time mm oat-target:
real 2m29.372s
user 10m3.130s
sys 5m46.290s
The speed increase is probably just noise.
Change-Id: If3c1280cbaa4c7e4361127d064ac744ea12cdf49
diff --git a/src/thread_pool.cc b/src/thread_pool.cc
new file mode 100644
index 0000000..fa0cf79
--- /dev/null
+++ b/src/thread_pool.cc
@@ -0,0 +1,124 @@
+#include "runtime.h"
+#include "stl_util.h"
+#include "thread.h"
+#include "thread_pool.h"
+
+namespace art {
+
+ThreadPoolWorker::ThreadPoolWorker(ThreadPool* thread_pool, const std::string& name,
+ size_t stack_size)
+ : thread_pool_(thread_pool),
+ name_(name),
+ stack_size_(stack_size) {
+ const char* reason = "new thread pool worker thread";
+ CHECK_PTHREAD_CALL(pthread_attr_init, (&attr), reason);
+ CHECK_PTHREAD_CALL(pthread_attr_setstacksize, (&attr, stack_size), reason);
+ CHECK_PTHREAD_CALL(pthread_create, (&pthread_, &attr, &Callback, this), reason);
+ CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attr), reason);
+}
+
+ThreadPoolWorker::~ThreadPoolWorker() {
+ CHECK_PTHREAD_CALL(pthread_join, (pthread_, NULL), "thread pool worker shutdown");
+}
+
+void ThreadPoolWorker::Run() {
+ Thread* self = Thread::Current();
+ Closure* task = NULL;
+ while ((task = thread_pool_->GetTask(self)) != NULL) {
+ task->Run(self);
+ }
+}
+
+void* ThreadPoolWorker::Callback(void* arg) {
+ ThreadPoolWorker* worker = reinterpret_cast<ThreadPoolWorker*>(arg);
+ Runtime* runtime = Runtime::Current();
+ CHECK(runtime->AttachCurrentThread(worker->name_.c_str(), true, NULL));
+ // Do work until its time to shut down.
+ worker->Run();
+ runtime->DetachCurrentThread();
+ return NULL;
+}
+
+void ThreadPool::AddTask(Thread* self, Closure* task){
+ MutexLock mu(self, task_queue_lock_);
+ tasks_.push_back(task);
+ // If we have any waiters, signal one.
+ if (waiting_count_ != 0) {
+ task_queue_condition_.Signal(self);
+ }
+}
+
+void ThreadPool::AddThread(size_t stack_size) {
+ threads_.push_back(
+ new ThreadPoolWorker(
+ this,
+ StringPrintf("Thread pool worker %d", static_cast<int>(GetThreadCount())),
+ stack_size));
+}
+
+ThreadPool::ThreadPool(size_t num_threads)
+ : task_queue_lock_("task queue lock"),
+ task_queue_condition_("task queue condition", task_queue_lock_),
+ completion_condition_("task completion condition", task_queue_lock_),
+ started_(false),
+ shutting_down_(false),
+ waiting_count_(0) {
+ while (GetThreadCount() < num_threads) {
+ AddThread(ThreadPoolWorker::kDefaultStackSize);
+ }
+}
+
+ThreadPool::~ThreadPool() {
+ // Tell any remaining workers to shut down.
+ shutting_down_ = true;
+ android_memory_barrier();
+ // Broadcast to everyone waiting.
+ task_queue_condition_.Broadcast(Thread::Current());
+ // Wait for the threads to finish.
+ STLDeleteElements(&threads_);
+}
+
+void ThreadPool::StartWorkers(Thread* self) {
+ MutexLock mu(self, task_queue_lock_);
+ started_ = true;
+ android_memory_barrier();
+ task_queue_condition_.Broadcast(self);
+}
+
+void ThreadPool::StopWorkers(Thread* self) {
+ MutexLock mu(self, task_queue_lock_);
+ started_ = false;
+ android_memory_barrier();
+}
+
+Closure* ThreadPool::GetTask(Thread* self) {
+ MutexLock mu(self, task_queue_lock_);
+ while (!shutting_down_) {
+ if (started_ && !tasks_.empty()) {
+ Closure* task = tasks_.front();
+ tasks_.pop_front();
+ return task;
+ }
+
+ waiting_count_++;
+ if (waiting_count_ == GetThreadCount() && tasks_.empty()) {
+ // We may be done, lets broadcast to the completion condition.
+ completion_condition_.Broadcast(self);
+ }
+ task_queue_condition_.Wait(self);
+ waiting_count_--;
+ }
+
+ // We are shutting down, return NULL to tell the worker thread to stop looping.
+ return NULL;
+}
+
+void ThreadPool::Wait(Thread* self) {
+ MutexLock mu(self, task_queue_lock_);
+ // Wait until each thread is waiting and the task list is empty.
+ while (waiting_count_ != GetThreadCount() || !tasks_.empty()) {
+ completion_condition_.Wait(self);
+ }
+}
+
+} // namespace art