[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