[Bf-blender-cvs] [6fc6153b500] temp-tbb-task-scheduler: Tasks: rewrite task scheduler and pools to use TBB
Brecht Van Lommel
noreply at git.blender.org
Tue Nov 5 15:11:03 CET 2019
Commit: 6fc6153b5003d283a6593a90c43c81cec4cb5379
Author: Brecht Van Lommel
Date: Sat Oct 12 17:11:36 2019 +0200
Branches: temp-tbb-task-scheduler
https://developer.blender.org/rB6fc6153b5003d283a6593a90c43c81cec4cb5379
Tasks: rewrite task scheduler and pools to use TBB
TODO: test performance
* Local queues in depsgraph
* Grain size in parallel range
* Iterators chunk size calc
===================================================================
M source/blender/blenlib/BLI_task.h
M source/blender/blenlib/CMakeLists.txt
M source/blender/blenlib/intern/task_pool.cc
A source/blender/blenlib/intern/task_scheduler.cc
M source/blender/blenlib/intern/threads.c
M source/blender/depsgraph/intern/eval/deg_eval.cc
M source/blender/editors/render/render_opengl.c
M source/creator/creator.c
M tests/gtests/blenlib/BLI_linklist_lockfree_test.cc
M tests/gtests/blenlib/BLI_task_performance_test.cc
M tests/gtests/blenlib/BLI_task_test.cc
===================================================================
diff --git a/source/blender/blenlib/BLI_task.h b/source/blender/blenlib/BLI_task.h
index e65d2e412cc..c55c7f27b97 100644
--- a/source/blender/blenlib/BLI_task.h
+++ b/source/blender/blenlib/BLI_task.h
@@ -37,28 +37,17 @@ struct BLI_mempool;
/* Task Scheduler
*
- * Central scheduler that holds running threads ready to execute tasks. A single
- * queue holds the task from all pools.
- *
- * Init/exit must be called before/after any task pools are created/freed, and
- * must be called from the main threads. All other scheduler and pool functions
- * are thread-safe. */
+ * Initialization must be called after command line arguments are parsed to
+ * control the number of threads, and before any task pools are created. */
typedef struct TaskScheduler TaskScheduler;
-enum {
- TASK_SCHEDULER_AUTO_THREADS = 0,
- TASK_SCHEDULER_SINGLE_THREAD = 1,
-};
-
-TaskScheduler *BLI_task_scheduler_create(int num_threads);
-void BLI_task_scheduler_free(TaskScheduler *scheduler);
-
+void BLI_task_scheduler_init(void);
int BLI_task_scheduler_num_threads(TaskScheduler *scheduler);
/* Task Pool
*
- * Pool of tasks that will be executed by the central TaskScheduler. For each
+ * Pool of tasks that will be executed by the central task scheduler. For each
* pool, we can wait for all tasks to be done, or cancel them before they are
* done.
*
@@ -80,12 +69,16 @@ typedef void (*TaskRunFunction)(TaskPool *__restrict pool, void *taskdata);
typedef void (*TaskFreeFunction)(TaskPool *__restrict pool, void *taskdata);
TaskPool *BLI_task_pool_create(TaskScheduler *scheduler, void *userdata, TaskPriority priority);
-TaskPool *BLI_task_pool_create_background(TaskScheduler *scheduler,
- void *userdata,
- TaskPriority priority);
TaskPool *BLI_task_pool_create_suspended(TaskScheduler *scheduler,
void *userdata,
TaskPriority priority);
+TaskPool *BLI_task_pool_create_no_threads(TaskScheduler *scheduler, void *userdata);
+TaskPool *BLI_task_pool_create_background(TaskScheduler *scheduler,
+ void *userdata,
+ TaskPriority priority);
+TaskPool *BLI_task_pool_create_background_serial(TaskScheduler *scheduler,
+ void *userdata,
+ TaskPriority priority);
void BLI_task_pool_free(TaskPool *pool);
void BLI_task_pool_push(TaskPool *pool,
@@ -96,8 +89,6 @@ void BLI_task_pool_push(TaskPool *pool,
/* work and wait until all tasks are done */
void BLI_task_pool_work_and_wait(TaskPool *pool);
-/* work and wait until all tasks are done, then reset to the initial suspended state */
-void BLI_task_pool_work_wait_and_reset(TaskPool *pool);
/* cancel all tasks, keep worker threads running */
void BLI_task_pool_cancel(TaskPool *pool);
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index ddcd9495d58..6ed68608eb5 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -122,6 +122,7 @@ set(SRC
intern/task_iterator.c
intern/task_pool.cc
intern/task_range.cc
+ intern/task_scheduler.cc
intern/threads.c
intern/time.c
intern/timecode.c
diff --git a/source/blender/blenlib/intern/task_pool.cc b/source/blender/blenlib/intern/task_pool.cc
index d2c44876855..d483f4468fe 100644
--- a/source/blender/blenlib/intern/task_pool.cc
+++ b/source/blender/blenlib/intern/task_pool.cc
@@ -17,7 +17,7 @@
/** \file
* \ingroup bli
*
- * A generic task system which can be used for any task based subsystem.
+ * Task pool to run tasks in parallel.
*/
#include <stdlib.h>
@@ -26,519 +26,345 @@
#include "DNA_listBase.h"
-#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_mempool.h"
#include "BLI_task.h"
#include "BLI_threads.h"
-#include "atomic_ops.h"
-
-/* Define this to enable some detailed statistic print. */
-#undef DEBUG_STATS
-
-/* Types */
-
-/* Number of per-thread pre-allocated tasks.
- *
- * For more details see description of TaskMemPool.
- */
-#define MEMPOOL_SIZE 256
-
-#ifndef NDEBUG
-# define ASSERT_THREAD_ID(scheduler, thread_id) \
- do { \
- if (!BLI_thread_is_main()) { \
- TaskThread *thread = (TaskThread *)pthread_getspecific(scheduler->tls_id_key); \
- if (thread == NULL) { \
- BLI_assert(thread_id == 0); \
- } \
- else { \
- BLI_assert(thread_id == thread->id); \
- } \
- } \
- else { \
- BLI_assert(thread_id == 0); \
- } \
- } while (false)
-#else
-# define ASSERT_THREAD_ID(scheduler, thread_id)
+#ifdef WITH_TBB
+/* Quiet top level deprecation message, unrelated to API usage here. */
+# define TBB_SUPPRESS_DEPRECATED_MESSAGES 1
+# include <tbb/tbb.h>
#endif
-typedef struct Task {
- struct Task *next, *prev;
+/* Task
+ *
+ * Unit of work to execute. This is a C++ class to work with TBB. */
+class Task {
+ public:
+ TaskPool *pool;
TaskRunFunction run;
void *taskdata;
bool free_taskdata;
TaskFreeFunction freedata;
- TaskPool *pool;
-} Task;
-
-/* This is a per-thread storage of pre-allocated tasks.
- *
- * The idea behind this is simple: reduce amount of malloc() calls when pushing
- * new task to the pool. This is done by keeping memory from the tasks which
- * were finished already, so instead of freeing that memory we put it to the
- * pool for the later re-use.
- *
- * The tricky part here is to avoid any inter-thread synchronization, hence no
- * lock must exist around this pool. The pool will become an owner of the pointer
- * from freed task, and only corresponding thread will be able to use this pool
- * (no memory stealing and such).
- *
- * This leads to the following use of the pool:
- *
- * - task_push() should provide proper thread ID from which the task is being
- * pushed from.
- *
- * - Task allocation function which check corresponding memory pool and if there
- * is any memory in there it'll mark memory as re-used, remove it from the pool
- * and use that memory for the new task.
- *
- * At this moment task queue owns the memory.
- *
- * - When task is done and task_free() is called the memory will be put to the
- * pool which corresponds to a thread which handled the task.
- */
-typedef struct TaskMemPool {
- /* Number of pre-allocated tasks in the pool. */
- int num_tasks;
- /* Pre-allocated task memory pointers. */
- Task *tasks[MEMPOOL_SIZE];
-} TaskMemPool;
-
-#ifdef DEBUG_STATS
-typedef struct TaskMemPoolStats {
- /* Number of allocations. */
- int num_alloc;
- /* Number of avoided allocations (pointer was re-used from the pool). */
- int num_reuse;
- /* Number of discarded memory due to pool saturation, */
- int num_discard;
-} TaskMemPoolStats;
-#endif
-struct TaskPool {
- TaskScheduler *scheduler;
-
- volatile size_t num;
- ThreadMutex num_mutex;
- ThreadCondition num_cond;
+ Task(TaskPool *pool,
+ TaskRunFunction run,
+ void *taskdata,
+ bool free_taskdata,
+ TaskFreeFunction freedata)
+ : pool(pool), run(run), taskdata(taskdata), free_taskdata(free_taskdata), freedata(freedata)
+ {
+ }
- void *userdata;
- ThreadMutex user_mutex;
+ ~Task()
+ {
+ if (free_taskdata) {
+ if (freedata) {
+ freedata(pool, taskdata);
+ }
+ else {
+ MEM_freeN(taskdata);
+ }
+ }
+ }
- volatile bool do_cancel;
- volatile bool do_work;
+ /* Move constructor. */
+ Task(Task &&other)
+ : pool(other.pool),
+ run(other.run),
+ taskdata(other.taskdata),
+ free_taskdata(other.free_taskdata),
+ freedata(other.freedata)
+ {
+ other.pool = NULL;
+ other.run = NULL;
+ other.taskdata = NULL;
+ other.free_taskdata = false;
+ other.freedata = NULL;
+ }
- volatile bool is_suspended;
- bool start_suspended;
- ListBase suspended_queue;
- size_t num_suspended;
+ /* Execute task. */
+ void operator()() const
+ {
+ run(pool, taskdata);
+ }
- TaskPriority priority;
+ /* For performance, ensure we never copy the task and only move it. */
+ Task(const Task &other) = delete;
+ Task &operator=(const Task &other) = delete;
+ Task &operator=(Task &&other) = delete;
+};
- /* If set, this pool may never be work_and_wait'ed, which means TaskScheduler
- * has to use its special background fallback thread in case we are in
- * single-threaded situation.
- */
- bool run_in_background;
+/* TBB Task Group.
+ *
+ * Subclass since there seems to be no other way to set priority. */
+
+#ifdef WITH_TBB
+class TBBTaskGroup : public tbb::task_group {
+ public:
+ TBBTaskGroup(TaskPriority priority)
+ {
+ switch (priority) {
+ case TASK_PRIORITY_LOW:
+ my_context.set_priority(tbb::priority_low);
+ break;
+ case TASK_PRIORITY_HIGH:
+ my_context.set_priority(tbb::priority_normal);
+ break;
+ }
+ }
- /* This is a task scheduler's ID of a thread at which pool was constructed.
- * It will be used to access task TLS.
- */
- int thread_id;
+ ~TBBTaskGroup()
+ {
+ }
};
+#endif
-struct TaskScheduler {
- pthread_t *threads;
- struct TaskThread *task_threads;
- int num_threads;
- bool background_thread_only;
+/* Task Pool */
- ListBase queue;
- ThreadMutex queue_mutex;
- ThreadCondition queue_cond;
+typedef enum TaskPoolType {
+ TASK_POOL_TBB,
+ TASK_POOL_TBB_SUSPENDED,
+ TASK_POOL_NO_THREADS,
+ TASK_POOL_BACKGROUND,
+ TASK_POOL_BACKGROUND_SERIAL,
+} TaskPoolType;
- ThreadMutex startup_mutex;
- ThreadCondition startup_cond;
- volatile int num_thread_started;
+struct TaskPool {
+ TaskPoolType type;
+ bool use_threads;
- volatile bool do_exit;
+ ThreadMutex user_mutex;
+ void *userdata;
- /* NOTE: In pthread's TLS we store the whole TaskThread structure. */
- pthread_key_t tls_id_key;
+ /* TBB task pool. */
+#ifdef WITH_TBB
+ TBBTaskGroup tbb_group;
+#endif
+ volatile bool is_suspended;
+ BLI_mempool *suspended_mempool;
+
+ /* Background task pool. */
+ ListBase background_
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-blender-cvs
mailing list