Blender V4.3
task_iterator.c
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11#include <stdlib.h>
12
13#include "MEM_guardedalloc.h"
14
15#include "BLI_mempool.h"
16#include "BLI_mempool_private.h"
17#include "BLI_task.h"
18#include "BLI_threads.h"
19
20/* -------------------------------------------------------------------- */
24/* Allows to avoid using malloc for userdata_chunk in tasks, when small enough. */
25#define MALLOCA(_size) ((_size) <= 8192) ? alloca(_size) : MEM_mallocN((_size), __func__)
26#define MALLOCA_FREE(_mem, _size) \
27 if (((_mem) != NULL) && ((_size) > 8192)) { \
28 MEM_freeN(_mem); \
29 } \
30 ((void)0)
31
34/* -------------------------------------------------------------------- */
42
43static void parallel_mempool_func(TaskPool *__restrict pool, void *taskdata)
44{
46 BLI_mempool_threadsafe_iter *iter = &((ParallelMempoolTaskData *)taskdata)->ts_iter;
47 TaskParallelTLS *tls = &((ParallelMempoolTaskData *)taskdata)->tls;
48
49 MempoolIterData *item;
50 while ((item = mempool_iter_threadsafe_step(iter)) != NULL) {
51 state->func(state->userdata, item, tls);
52 }
53}
54
56 void *userdata,
58 const TaskParallelSettings *settings)
59{
60 if (UNLIKELY(BLI_mempool_len(mempool) == 0)) {
61 return;
62 }
63
64 void *userdata_chunk = settings->userdata_chunk;
65 const size_t userdata_chunk_size = settings->userdata_chunk_size;
66 void *userdata_chunk_array = NULL;
67 const bool use_userdata_chunk = (userdata_chunk_size != 0) && (userdata_chunk != NULL);
68
69 if (!settings->use_threading) {
70 TaskParallelTLS tls = {NULL};
71 if (use_userdata_chunk) {
72 if (settings->func_init != NULL) {
73 settings->func_init(userdata, userdata_chunk);
74 }
75 tls.userdata_chunk = userdata_chunk;
76 }
77
79 BLI_mempool_iternew(mempool, &iter);
80
81 void *item;
82 while ((item = BLI_mempool_iterstep(&iter))) {
83 func(userdata, item, &tls);
84 }
85
86 if (use_userdata_chunk) {
87 if (settings->func_free != NULL) {
88 /* `func_free` should only free data that was created during execution of `func`. */
89 settings->func_free(userdata, userdata_chunk);
90 }
91 }
92
93 return;
94 }
95
98 const int threads_num = BLI_task_scheduler_num_threads();
99
100 /* The idea here is to prevent creating task for each of the loop iterations
101 * and instead have tasks which are evenly distributed across CPU cores and
102 * pull next item to be crunched using the threaded-aware BLI_mempool_iter.
103 */
104 const int tasks_num = threads_num + 2;
105
106 state.userdata = userdata;
107 state.func = func;
108
109 if (use_userdata_chunk) {
110 userdata_chunk_array = MALLOCA(userdata_chunk_size * tasks_num);
111 }
112
114 mempool, (size_t)tasks_num);
115
116 for (int i = 0; i < tasks_num; i++) {
117 void *userdata_chunk_local = NULL;
118 if (use_userdata_chunk) {
119 userdata_chunk_local = (char *)userdata_chunk_array + (userdata_chunk_size * i);
120 memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size);
121 if (settings->func_init != NULL) {
122 settings->func_init(userdata, userdata_chunk_local);
123 }
124 }
125 mempool_iterator_data[i].tls.userdata_chunk = userdata_chunk_local;
126
127 /* Use this pool's pre-allocated tasks. */
128 BLI_task_pool_push(task_pool, parallel_mempool_func, &mempool_iterator_data[i], false, NULL);
129 }
130
133
134 if (use_userdata_chunk) {
135 if ((settings->func_free != NULL) || (settings->func_reduce != NULL)) {
136 for (int i = 0; i < tasks_num; i++) {
137 if (settings->func_reduce) {
138 settings->func_reduce(
139 userdata, userdata_chunk, mempool_iterator_data[i].tls.userdata_chunk);
140 }
141 if (settings->func_free) {
142 settings->func_free(userdata, mempool_iterator_data[i].tls.userdata_chunk);
143 }
144 }
145 }
146 MALLOCA_FREE(userdata_chunk_array, userdata_chunk_size * tasks_num);
147 }
148
149 mempool_iter_threadsafe_destroy(mempool_iterator_data);
150}
151
152#undef MALLOCA
153#undef MALLOCA_FREE
154
ParallelMempoolTaskData * mempool_iter_threadsafe_create(BLI_mempool *pool, const size_t iter_num)
void mempool_iter_threadsafe_destroy(ParallelMempoolTaskData *iter_arr)
void * mempool_iter_threadsafe_step(BLI_mempool_threadsafe_iter *ts_iter)
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()
int BLI_mempool_len(const BLI_mempool *pool) ATTR_NONNULL(1)
int BLI_task_scheduler_num_threads(void)
@ TASK_PRIORITY_HIGH
Definition BLI_task.h:57
void * BLI_task_pool_user_data(TaskPool *pool)
Definition task_pool.cc:516
struct MempoolIterData MempoolIterData
Definition BLI_task.h:209
void BLI_task_pool_work_and_wait(TaskPool *pool)
Definition task_pool.cc:471
TaskPool * BLI_task_pool_create(void *userdata, eTaskPriority priority)
Definition task_pool.cc:394
void(* TaskParallelMempoolFunc)(void *userdata, MempoolIterData *iter, const TaskParallelTLS *__restrict tls)
Definition BLI_task.h:211
void BLI_task_pool_free(TaskPool *pool)
Definition task_pool.cc:431
void BLI_task_pool_push(TaskPool *pool, TaskRunFunction run, void *taskdata, bool free_taskdata, TaskFreeFunction freedata)
Definition task_pool.cc:450
#define UNLIKELY(x)
Read Guarded memory(de)allocation.
#define NULL
TaskPool * task_pool
static ulong state[N]
TaskParallelMempoolFunc func
void * userdata_chunk
Definition BLI_task.h:146
#define MALLOCA_FREE(_mem, _size)
static void parallel_mempool_func(TaskPool *__restrict pool, void *taskdata)
#define MALLOCA(_size)
struct ParallelMempoolState ParallelMempoolState
void BLI_task_parallel_mempool(BLI_mempool *mempool, void *userdata, TaskParallelMempoolFunc func, const TaskParallelSettings *settings)