[Bf-blender-cvs] [b84e6dfee4a] master: Add ability to use more than one mempool iterator simultaneously.

Bastien Montagne noreply at git.blender.org
Thu Nov 23 21:36:08 CET 2017


Commit: b84e6dfee4a82b4142651710cbe842f8d021a861
Author: Bastien Montagne
Date:   Thu Nov 23 21:12:00 2017 +0100
Branches: master
https://developer.blender.org/rBb84e6dfee4a82b4142651710cbe842f8d021a861

Add ability to use more than one mempool iterator simultaneously.

This will allow threaded tasks to 'consume' all mempool items in
parallel tasks, each one working on a whole chunk at once (to reduce
concurrency managing overhead).

===================================================================

M	source/blender/blenlib/BLI_mempool.h
M	source/blender/blenlib/intern/BLI_mempool.c

===================================================================

diff --git a/source/blender/blenlib/BLI_mempool.h b/source/blender/blenlib/BLI_mempool.h
index 0c754f551e0..b68ca6b1f2b 100644
--- a/source/blender/blenlib/BLI_mempool.h
+++ b/source/blender/blenlib/BLI_mempool.h
@@ -71,6 +71,8 @@ typedef struct BLI_mempool_iter {
 	BLI_mempool *pool;
 	struct BLI_mempool_chunk *curchunk;
 	unsigned int curindex;
+
+	struct BLI_mempool_chunk **curchunk_threaded_shared;
 } BLI_mempool_iter;
 
 /* flag */
@@ -87,6 +89,9 @@ enum {
 void  BLI_mempool_iternew(BLI_mempool *pool, BLI_mempool_iter *iter) ATTR_NONNULL();
 void *BLI_mempool_iterstep(BLI_mempool_iter *iter) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
 
+BLI_mempool_iter *BLI_mempool_iter_threadsafe_create(BLI_mempool *pool, const size_t num_iter) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+void  BLI_mempool_iter_threadsafe_free(BLI_mempool_iter *iter_arr) ATTR_NONNULL();
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/source/blender/blenlib/intern/BLI_mempool.c b/source/blender/blenlib/intern/BLI_mempool.c
index b02811616dd..c90f9e300b7 100644
--- a/source/blender/blenlib/intern/BLI_mempool.c
+++ b/source/blender/blenlib/intern/BLI_mempool.c
@@ -41,6 +41,8 @@
 #include <string.h>
 #include <stdlib.h>
 
+#include "atomic_ops.h"
+
 #include "BLI_utildefines.h"
 
 #include "BLI_mempool.h" /* own include */
@@ -553,7 +555,7 @@ void *BLI_mempool_as_arrayN(BLI_mempool *pool, const char *allocstr)
 }
 
 /**
- * Create a new mempool iterator, \a BLI_MEMPOOL_ALLOW_ITER flag must be set.
+ * Initialize a new mempool iterator, \a BLI_MEMPOOL_ALLOW_ITER flag must be set.
  */
 void BLI_mempool_iternew(BLI_mempool *pool, BLI_mempool_iter *iter)
 {
@@ -562,6 +564,47 @@ void BLI_mempool_iternew(BLI_mempool *pool, BLI_mempool_iter *iter)
 	iter->pool = pool;
 	iter->curchunk = pool->chunks;
 	iter->curindex = 0;
+
+	iter->curchunk_threaded_shared = NULL;
+}
+
+/**
+ * Initialize an array of mempool iterators, \a BLI_MEMPOOL_ALLOW_ITER flag must be set.
+ *
+ * This is used in threaded code, to generate as much iterators as needed (each task should have its own),
+ * such that each iterator goes over its own single chunk, and only getting the next chunk to iterate over has to be
+ * protected against concurrency (which can be done in a lockless way).
+ *
+ * To be used when creating a task for each single item in the pool is totally overkill.
+ *
+ * See BLI_task_parallel_mempool implementation for detailed usage example.
+ */
+BLI_mempool_iter *BLI_mempool_iter_threadsafe_create(BLI_mempool *pool, const size_t num_iter)
+{
+	BLI_assert(pool->flag & BLI_MEMPOOL_ALLOW_ITER);
+
+	BLI_mempool_iter *iter_arr = MEM_mallocN(sizeof(*iter_arr) * num_iter, __func__);
+	BLI_mempool_chunk **curchunk_threaded_shared = MEM_mallocN(sizeof(void *), __func__);
+
+	BLI_mempool_iternew(pool, iter_arr);
+
+	*curchunk_threaded_shared = iter_arr->curchunk;
+	iter_arr->curchunk_threaded_shared = curchunk_threaded_shared;
+
+	for (size_t i = 1; i < num_iter; i++) {
+		iter_arr[i] = iter_arr[0];
+		*curchunk_threaded_shared = iter_arr[i].curchunk = (*curchunk_threaded_shared) ? (*curchunk_threaded_shared)->next : NULL;
+	}
+
+	return iter_arr;
+}
+
+void  BLI_mempool_iter_threadsafe_free(BLI_mempool_iter *iter_arr)
+{
+	BLI_assert(iter_arr->curchunk_threaded_shared != NULL);
+
+	MEM_freeN(iter_arr->curchunk_threaded_shared);
+	MEM_freeN(iter_arr);
 }
 
 #if 0
@@ -571,15 +614,28 @@ static void *bli_mempool_iternext(BLI_mempool_iter *iter)
 {
 	void *ret = NULL;
 
-	if (!iter->curchunk || !iter->pool->totused) return NULL;
+	if (iter->curchunk == NULL || !iter->pool->totused) {
+		return ret;
+	}
 
 	ret = ((char *)CHUNK_DATA(iter->curchunk)) + (iter->pool->esize * iter->curindex);
 
 	iter->curindex++;
 
 	if (iter->curindex == iter->pool->pchunk) {
-		iter->curchunk = iter->curchunk->next;
 		iter->curindex = 0;
+		if (iter->curchunk_threaded_shared) {
+			while (1) {
+				iter->curchunk = *iter->curchunk_threaded_shared;
+				if (iter->curchunk == NULL) {
+					break;
+				}
+				if (atomic_cas_ptr((void **)iter->curchunk_threaded_shared, iter->curchunk, iter->curchunk->next) == iter->curchunk) {
+					break;
+				}
+			}
+		}
+		iter->curchunk = iter->curchunk->next;
 	}
 
 	return ret;
@@ -620,8 +676,18 @@ void *BLI_mempool_iterstep(BLI_mempool_iter *iter)
 		}
 		else {
 			iter->curindex = 0;
+			if (iter->curchunk_threaded_shared) {
+				for (iter->curchunk = *iter->curchunk_threaded_shared;
+				     (iter->curchunk != NULL) &&
+				     (atomic_cas_ptr((void **)iter->curchunk_threaded_shared, iter->curchunk, iter->curchunk->next) != iter->curchunk);
+				     iter->curchunk = *iter->curchunk_threaded_shared);
+
+				if (UNLIKELY(iter->curchunk == NULL)) {
+					return (ret->freeword == FREEWORD) ? NULL : ret;
+				}
+			}
 			iter->curchunk = iter->curchunk->next;
-			if (iter->curchunk == NULL) {
+			if (UNLIKELY(iter->curchunk == NULL)) {
 				return (ret->freeword == FREEWORD) ? NULL : ret;
 			}
 			curnode = CHUNK_DATA(iter->curchunk);



More information about the Bf-blender-cvs mailing list