Переглянути джерело

Optimize the ThreadHive and change the dependency system

Panagiotis Christopoulos Charitos 7 роки тому
батько
коміт
aa66fb86a6

+ 18 - 23
src/anki/scene/Visibility.cpp

@@ -63,7 +63,7 @@ void VisibilityContext::submitNewWork(FrustumComponent& frc, RenderQueue& rqueue
 
 	// Software rasterizer tasks
 	SoftwareRasterizer* r = nullptr;
-	Array<ThreadHiveDependencyHandle, ThreadHive::MAX_THREADS> rasterizeDeps;
+	ThreadHiveSemaphore* rasterizeSem = nullptr;
 	if(frc.visibilityTestsEnabled(FrustumComponentVisibilityTestFlag::OCCLUDERS))
 	{
 		// Gather triangles task
@@ -77,11 +77,13 @@ void VisibilityContext::submitNewWork(FrustumComponent& frc, RenderQueue& rqueue
 		ThreadHiveTask gatherTask;
 		gatherTask.m_callback = GatherVisibleTrianglesTask::callback;
 		gatherTask.m_argument = gather;
+		gatherTask.m_signalSemaphore = hive.newSemaphore(1);
 
 		hive.submitTasks(&gatherTask, 1);
 
-		// Rasterize triangles task
+		// Rasterize triangles tasks
 		U count = hive.getThreadCount();
+		rasterizeSem = hive.newSemaphore(count);
 		RasterizeTrianglesTask* rasterize = alloc.newArray<RasterizeTrianglesTask>(count);
 
 		Array<ThreadHiveTask, ThreadHive::MAX_THREADS> rastTasks;
@@ -92,20 +94,18 @@ void VisibilityContext::submitNewWork(FrustumComponent& frc, RenderQueue& rqueue
 			rast.m_taskIdx = count;
 			rast.m_taskCount = hive.getThreadCount();
 
-			rastTasks[count].m_callback = RasterizeTrianglesTask::callback;
-			rastTasks[count].m_argument = &rast;
-			rastTasks[count].m_inDependencies = WeakArray<ThreadHiveDependencyHandle>(&gatherTask.m_outDependency, 1);
+			ThreadHiveTask& task = rastTasks[count];
+			task.m_callback = RasterizeTrianglesTask::callback;
+			task.m_argument = &rast;
+			task.m_waitSemaphore = gatherTask.m_signalSemaphore;
+			task.m_signalSemaphore = rasterizeSem;
 		}
 
 		count = hive.getThreadCount();
 		hive.submitTasks(&rastTasks[0], count);
-		while(count--)
-		{
-			rasterizeDeps[count] = rastTasks[count].m_outDependency;
-		}
 	}
 
-	// Gather visibles from octree
+	// Gather visibles from the octree
 	GatherVisiblesFromOctreeTask* gather = alloc.newInstance<GatherVisiblesFromOctreeTask>();
 	gather->m_visCtx = this;
 	gather->m_frc = &frc;
@@ -114,10 +114,8 @@ void VisibilityContext::submitNewWork(FrustumComponent& frc, RenderQueue& rqueue
 	ThreadHiveTask gatherTask;
 	gatherTask.m_callback = GatherVisiblesFromOctreeTask::callback;
 	gatherTask.m_argument = gather;
-	if(r)
-	{
-		gatherTask.m_inDependencies = WeakArray<ThreadHiveDependencyHandle>(&rasterizeDeps[0], hive.getThreadCount());
-	}
+	gatherTask.m_signalSemaphore = hive.newSemaphore(1);
+	gatherTask.m_waitSemaphore = rasterizeSem;
 
 	hive.submitTasks(&gatherTask, 1);
 
@@ -125,10 +123,11 @@ void VisibilityContext::submitNewWork(FrustumComponent& frc, RenderQueue& rqueue
 	U testCount = hive.getThreadCount();
 	WeakArray<VisibilityTestTask> tests(alloc.newArray<VisibilityTestTask>(testCount), testCount);
 	WeakArray<ThreadHiveTask> testTasks(alloc.newArray<ThreadHiveTask>(testCount), testCount);
+	ThreadHiveSemaphore* testSem = hive.newSemaphore(testCount);
 
 	for(U i = 0; i < testCount; ++i)
 	{
-		auto& test = tests[i];
+		VisibilityTestTask& test = tests[i];
 		test.m_visCtx = this;
 		test.m_frc = &frc;
 		test.m_visibleSpatialComponents = &gather->m_visibleSpatialComponents;
@@ -136,10 +135,11 @@ void VisibilityContext::submitNewWork(FrustumComponent& frc, RenderQueue& rqueue
 		test.m_taskCount = testCount;
 		test.m_r = r;
 
-		auto& task = testTasks[i];
+		ThreadHiveTask& task = testTasks[i];
 		task.m_callback = VisibilityTestTask::callback;
 		task.m_argument = &test;
-		task.m_inDependencies = WeakArray<ThreadHiveDependencyHandle>(&gatherTask.m_outDependency, 1);
+		task.m_waitSemaphore = gatherTask.m_signalSemaphore;
+		task.m_signalSemaphore = testSem;
 	}
 
 	hive.submitTasks(&testTasks[0], testCount);
@@ -155,12 +155,7 @@ void VisibilityContext::submitNewWork(FrustumComponent& frc, RenderQueue& rqueue
 	ThreadHiveTask combineTask;
 	combineTask.m_callback = CombineResultsTask::callback;
 	combineTask.m_argument = combine;
-	combineTask.m_inDependencies =
-		WeakArray<ThreadHiveDependencyHandle>(alloc.newArray<ThreadHiveDependencyHandle>(testCount), testCount);
-	for(U i = 0; i < testCount; ++i)
-	{
-		combineTask.m_inDependencies[i] = testTasks[i].m_outDependency;
-	}
+	combineTask.m_waitSemaphore = testSem;
 
 	hive.submitTasks(&combineTask, 1);
 }

+ 7 - 6
src/anki/scene/VisibilityInternal.h

@@ -20,6 +20,7 @@ namespace anki
 // Forward
 class FrustumComponent;
 class ThreadHive;
+class ThreadHiveSemaphore;
 
 /// @addtogroup scene
 /// @{
@@ -149,7 +150,7 @@ public:
 	Atomic<U32> m_rasterizedVertCount = {0}; ///< That will be used by the RasterizeTrianglesTask.
 
 	/// Thread hive task.
-	static void callback(void* ud, U32 threadId, ThreadHive& hive)
+	static void callback(void* ud, U32 threadId, ThreadHive& hive, ThreadHiveSemaphore* sem)
 	{
 		GatherVisibleTrianglesTask& self = *static_cast<GatherVisibleTrianglesTask*>(ud);
 		self.gather();
@@ -168,7 +169,7 @@ public:
 	U32 m_taskCount;
 
 	/// Thread hive task.
-	static void callback(void* ud, U32 threadId, ThreadHive& hive)
+	static void callback(void* ud, U32 threadId, ThreadHive& hive, ThreadHiveSemaphore* sem)
 	{
 		RasterizeTrianglesTask& self = *static_cast<RasterizeTrianglesTask*>(ud);
 		self.rasterize();
@@ -188,7 +189,7 @@ public:
 	WeakArray<void*> m_visibleSpatialComponents; ///< The results of the task.
 
 	/// Thread hive task.
-	static void callback(void* ud, U32 threadId, ThreadHive& hive)
+	static void callback(void* ud, U32 threadId, ThreadHive& hive, ThreadHiveSemaphore* sem)
 	{
 		GatherVisiblesFromOctreeTask& self = *static_cast<GatherVisiblesFromOctreeTask*>(ud);
 		self.gather();
@@ -208,7 +209,7 @@ public:
 	SoftwareRasterizer* m_r;
 
 	/// Thread hive task.
-	static void callback(void* ud, U32 threadId, ThreadHive& hive)
+	static void callback(void* ud, U32 threadId, ThreadHive& hive, ThreadHiveSemaphore* sem)
 	{
 		GatherVisiblesFromSectorsTask& self = *static_cast<GatherVisiblesFromSectorsTask*>(ud);
 		self.gather();
@@ -238,7 +239,7 @@ public:
 	SoftwareRasterizer* m_r ANKI_DBG_NULLIFY;
 
 	/// Thread hive task.
-	static void callback(void* ud, U32 threadId, ThreadHive& hive)
+	static void callback(void* ud, U32 threadId, ThreadHive& hive, ThreadHiveSemaphore* sem)
 	{
 		VisibilityTestTask& self = *static_cast<VisibilityTestTask*>(ud);
 		self.test(hive);
@@ -267,7 +268,7 @@ public:
 	SoftwareRasterizer* m_swRast = nullptr; ///< For cleanup.
 
 	/// Thread hive task.
-	static void callback(void* ud, U32 threadId, ThreadHive& hive)
+	static void callback(void* ud, U32 threadId, ThreadHive& hive, ThreadHiveSemaphore* sem)
 	{
 		CombineResultsTask& self = *static_cast<CombineResultsTask*>(ud);
 		self.combine();

+ 22 - 88
src/anki/util/ThreadHive.cpp

@@ -32,7 +32,7 @@ public:
 		, m_hive(hive)
 	{
 		ANKI_ASSERT(hive);
-		m_thread.start(this, threadCallback, (pinToCores) ? m_id : -1);
+		m_thread.start(this, threadCallback, (pinToCores) ? I(m_id) : -1);
 	}
 
 private:
@@ -46,7 +46,7 @@ private:
 	}
 };
 
-class ThreadHive::Task
+class ThreadHive::Task : public NonCopyable
 {
 public:
 	Task* m_next; ///< Next in the list.
@@ -54,21 +54,8 @@ public:
 	ThreadHiveTaskCallback m_cb; ///< Callback that defines the task.
 	void* m_arg; ///< Args for the callback.
 
-	Task** m_deps;
-	U16 m_depCount;
-	Bool8 m_othersDepend; ///< Other tasks depend on this one.
-
-	Task()
-	{
-	}
-
-	Task(const Task& b) = delete;
-	Task& operator=(const Task& b) = delete;
-
-	Bool done() const
-	{
-		return m_cb == nullptr;
-	}
+	ThreadHiveSemaphore* m_waitSemaphore;
+	ThreadHiveSemaphore* m_signalSemaphore;
 };
 
 ThreadHive::ThreadHive(U threadCount, GenericMemoryPoolAllocator<U8> alloc, Bool pinToCores)
@@ -117,47 +104,17 @@ void ThreadHive::submitTasks(ThreadHiveTask* tasks, const U taskCount)
 	// Allocate tasks
 	Task* const htasks = m_alloc.newArray<Task>(taskCount);
 
-	// Allocate the dependency handles
-	U depCount = 0;
-	for(U i = 0; i < taskCount; ++i)
-	{
-		depCount += tasks[i].m_inDependencies.getSize();
-	}
-
-	Task** depHandles;
-	if(depCount)
-	{
-		depHandles = m_alloc.newArray<Task*>(depCount);
-	}
-	else
-	{
-		depHandles = nullptr;
-	}
-
-	depCount = 0;
-
 	// Initialize tasks
 	for(U i = 0; i < taskCount; ++i)
 	{
 		const ThreadHiveTask& inTask = tasks[i];
 		Task& outTask = htasks[i];
 
+		outTask.m_next = nullptr;
 		outTask.m_cb = inTask.m_callback;
 		outTask.m_arg = inTask.m_argument;
-		outTask.m_depCount = 0;
-		outTask.m_next = nullptr;
-		outTask.m_othersDepend = false;
-
-		// Set the dependencies
-		if(inTask.m_inDependencies.getSize() > 0)
-		{
-			outTask.m_deps = &depHandles[depCount];
-			depCount += inTask.m_inDependencies.getSize();
-		}
-		else
-		{
-			outTask.m_deps = nullptr;
-		}
+		outTask.m_waitSemaphore = inTask.m_waitSemaphore;
+		outTask.m_signalSemaphore = inTask.m_signalSemaphore;
 	}
 
 	// Push work
@@ -166,22 +123,8 @@ void ThreadHive::submitTasks(ThreadHiveTask* tasks, const U taskCount)
 
 		for(U i = 0; i < taskCount; ++i)
 		{
-			const ThreadHiveTask& inTask = tasks[i];
 			Task& outTask = htasks[i];
 
-			for(U j = 0; j < inTask.m_inDependencies.getSize(); ++j)
-			{
-				ThreadHiveDependencyHandle dep = inTask.m_inDependencies[j];
-				ANKI_ASSERT(dep);
-				Task* depTask = static_cast<Task*>(dep);
-
-				if(!depTask->done())
-				{
-					outTask.m_deps[outTask.m_depCount++] = depTask;
-					depTask->m_othersDepend = true;
-				}
-			}
-
 			// Push to the list
 			ANKI_HIVE_DEBUG_PRINT(
 				"pushing back %p (udata %p)\n", static_cast<void*>(&outTask), static_cast<void*>(outTask.m_arg));
@@ -205,12 +148,6 @@ void ThreadHive::submitTasks(ThreadHiveTask* tasks, const U taskCount)
 		// Notify all threads
 		m_cvar.notifyAll();
 	}
-
-	// Set the out dependencies
-	for(U i = 0; i < taskCount; ++i)
-	{
-		tasks[i].m_outDependency = static_cast<ThreadHiveDependencyHandle>(&htasks[i]);
-	}
 }
 
 void ThreadHive::threadRun(U threadId)
@@ -223,7 +160,16 @@ void ThreadHive::threadRun(U threadId)
 		ANKI_ASSERT(task && task->m_cb);
 		ANKI_HIVE_DEBUG_PRINT(
 			"tid: %lu will exec %p (udata: %p)\n", threadId, static_cast<void*>(task), static_cast<void*>(task->m_arg));
-		task->m_cb(task->m_arg, threadId, *this);
+		task->m_cb(task->m_arg, threadId, *this, task->m_signalSemaphore);
+
+		// Signal the semaphore as early as possible
+		if(task->m_signalSemaphore)
+		{
+			const U32 out = task->m_signalSemaphore->m_atomic.fetchSub(1);
+			(void)out;
+			ANKI_ASSERT(out > 0u);
+			ANKI_HIVE_DEBUG_PRINT("\tsem is %u\n", out - 1u);
+		}
 	}
 
 	ANKI_HIVE_DEBUG_PRINT("tid: %lu thread quits!\n", threadId);
@@ -238,12 +184,14 @@ Bool ThreadHive::waitForWork(U threadId, Task*& task)
 	// Complete the previous task
 	if(task)
 	{
+#if ANKI_EXTRA_CHECKS
 		task->m_cb = nullptr;
+#endif
 		--m_pendingTasks;
 
-		if(task->m_othersDepend || m_pendingTasks == 0)
+		if(task->m_signalSemaphore || m_pendingTasks == 0)
 		{
-			// A dependency got resolved or we are out of tasks. Wake them all
+			// A dependency maybe got resolved or we are out of tasks. Wake them all
 			ANKI_HIVE_DEBUG_PRINT("tid: %lu wake all\n", threadId);
 			m_cvar.notifyAll();
 		}
@@ -266,20 +214,8 @@ ThreadHive::Task* ThreadHive::getNewTask()
 	Task* task = m_head;
 	while(task)
 	{
-		ANKI_ASSERT(!task->done());
-
 		// Check if there are dependencies
-		Bool allDepsCompleted = true;
-		for(U j = 0; j < task->m_depCount; ++j)
-		{
-			Task* depTask = task->m_deps[j];
-
-			if(!depTask->done())
-			{
-				allDepsCompleted = false;
-				break;
-			}
-		}
+		const Bool allDepsCompleted = task->m_waitSemaphore == nullptr || task->m_waitSemaphore->m_atomic.load() == 0;
 
 		if(allDepsCompleted)
 		{
@@ -324,8 +260,6 @@ void ThreadHive::waitAllTasks()
 
 	m_head = nullptr;
 	m_tail = nullptr;
-	m_allocatedTasks = 0;
-	m_allocatedDeps = 0;
 	m_alloc.getMemoryPool().reset();
 
 	ANKI_HIVE_DEBUG_PRINT("mt: done waiting all\n");

+ 36 - 8
src/anki/util/ThreadHive.h

@@ -19,11 +19,28 @@ class ThreadHive;
 /// @{
 
 /// Opaque handle that defines a ThreadHive depedency. @memberof ThreadHive
-using ThreadHiveDependencyHandle = void*;
+class ThreadHiveSemaphore
+{
+	friend class ThreadHive;
+
+public:
+	/// Increase the value of the semaphore. It's easy to brake things with that.
+	void increaseSemaphore()
+	{
+		m_atomic.fetchAdd(1);
+	}
+
+private:
+	Atomic<U32> m_atomic;
+
+	// No need to construct it or delete it
+	ThreadHiveSemaphore() = delete;
+	~ThreadHiveSemaphore() = delete;
+};
 
 /// The callback that defines a ThreadHibe task.
 /// @memberof ThreadHive
-using ThreadHiveTaskCallback = void (*)(void*, U32 threadId, ThreadHive& hive);
+using ThreadHiveTaskCallback = void (*)(void*, U32 threadId, ThreadHive& hive, ThreadHiveSemaphore* signalSemaphore);
 
 /// Task for the ThreadHive. @memberof ThreadHive
 class ThreadHiveTask
@@ -35,11 +52,12 @@ public:
 	/// Arguments to pass to the m_callback.
 	void* m_argument ANKI_DBG_NULLIFY;
 
-	/// The tasks that this task will depend on.
-	WeakArray<ThreadHiveDependencyHandle> m_inDependencies;
+	/// The task will start when that semaphore reaches zero.
+	ThreadHiveSemaphore* m_waitSemaphore = nullptr;
 
-	/// Will be filled after the submission of the task. Can be used to set dependencies to future tasks.
-	ThreadHiveDependencyHandle m_outDependency;
+	/// When the task is completed that semaphore will be decremented by one. Can be used to set dependencies to future
+	/// tasks.
+	ThreadHiveSemaphore* m_signalSemaphore = nullptr;
 };
 
 /// A scheduler of small tasks. It takes a number of tasks and schedules them in one of the threads. The tasks can
@@ -59,6 +77,18 @@ public:
 		return m_threadCount;
 	}
 
+	/// Create a new semaphore with some initial value.
+	/// @param initialValue  Can't be zero.
+	ThreadHiveSemaphore* newSemaphore(const U32 initialValue)
+	{
+		ANKI_ASSERT(initialValue > 0);
+		PtrSize alignment = alignof(ThreadHiveSemaphore);
+		ThreadHiveSemaphore* sem =
+			reinterpret_cast<ThreadHiveSemaphore*>(m_alloc.allocate(sizeof(ThreadHiveSemaphore), &alignment));
+		sem->m_atomic.set(initialValue);
+		return sem;
+	}
+
 	/// Submit tasks. The ThreadHiveTaskCallback callbacks can also call this.
 	void submitTasks(ThreadHiveTask* tasks, const U taskCount);
 
@@ -89,8 +119,6 @@ private:
 	Task* m_tail = nullptr; ///< Tail of the task list.
 	Bool m_quit = false;
 	U32 m_pendingTasks = 0;
-	U32 m_allocatedTasks = 0;
-	U32 m_allocatedDeps = 0;
 
 	Mutex m_mtx;
 	ConditionVariable m_cvar;

+ 1 - 1
src/anki/util/ThreadPosix.cpp

@@ -88,7 +88,7 @@ void Thread::start(void* userData, ThreadCallback callback, I pinToCore)
 	I err = pthread_create(thread, &attr, pthreadCallback, this);
 	if(err)
 	{
-		ANKI_UTIL_LOGF("pthread_create() failed");
+		ANKI_UTIL_LOGF("pthread_create() failed: %d", err);
 	}
 	else
 	{

+ 1 - 1
tests/gr/StackGpuAllocator.cpp

@@ -75,7 +75,7 @@ public:
 	Atomic<U32> m_allocCount;
 };
 
-static void doAllocation(void* arg, U32 threadId, ThreadHive& hive)
+static void doAllocation(void* arg, U32 threadId, ThreadHive& hive, ThreadHiveSemaphore* sem)
 {
 	TestContext* ctx = static_cast<TestContext*>(arg);
 

+ 17 - 13
tests/util/ThreadHive.cpp

@@ -29,13 +29,13 @@ public:
 	};
 };
 
-static void decNumber(void* arg, U32, ThreadHive& hive)
+static void decNumber(void* arg, U32, ThreadHive& hive, ThreadHiveSemaphore* sem)
 {
 	ThreadHiveTestContext* ctx = static_cast<ThreadHiveTestContext*>(arg);
 	ctx->m_countAtomic.fetchSub(2);
 }
 
-static void incNumber(void* arg, U32, ThreadHive& hive)
+static void incNumber(void* arg, U32, ThreadHive& hive, ThreadHiveSemaphore* sem)
 {
 	ThreadHiveTestContext* ctx = static_cast<ThreadHiveTestContext*>(arg);
 	ctx->m_countAtomic.fetchAdd(4);
@@ -43,7 +43,7 @@ static void incNumber(void* arg, U32, ThreadHive& hive)
 	hive.submitTask(decNumber, arg);
 }
 
-static void taskToWaitOn(void* arg, U32, ThreadHive& hive)
+static void taskToWaitOn(void* arg, U32, ThreadHive& hive, ThreadHiveSemaphore* sem)
 {
 	ThreadHiveTestContext* ctx = static_cast<ThreadHiveTestContext*>(arg);
 	HighRezTimer::sleep(1.0);
@@ -51,7 +51,7 @@ static void taskToWaitOn(void* arg, U32, ThreadHive& hive)
 	HighRezTimer::sleep(0.1);
 }
 
-static void taskToWait(void* arg, U32 threadId, ThreadHive& hive)
+static void taskToWait(void* arg, U32 threadId, ThreadHive& hive, ThreadHiveSemaphore* sem)
 {
 	ThreadHiveTestContext* ctx = static_cast<ThreadHiveTestContext*>(arg);
 	U prev = ctx->m_countAtomic.fetchAdd(1);
@@ -90,17 +90,20 @@ ANKI_TEST(Util, ThreadHive)
 		ThreadHiveTask task;
 		task.m_callback = taskToWaitOn;
 		task.m_argument = &ctx;
+		task.m_signalSemaphore = hive.newSemaphore(1);
 
 		hive.submitTasks(&task, 1);
 
 		const U DEP_TASKS = 10;
 		ThreadHiveTask dtasks[DEP_TASKS];
+		ThreadHiveSemaphore* sem = hive.newSemaphore(DEP_TASKS);
 
 		for(U i = 0; i < DEP_TASKS; ++i)
 		{
 			dtasks[i].m_callback = taskToWait;
 			dtasks[i].m_argument = &ctx;
-			dtasks[i].m_inDependencies = WeakArray<ThreadHiveDependencyHandle>(&task.m_outDependency, 1);
+			dtasks[i].m_waitSemaphore = task.m_signalSemaphore;
+			dtasks[i].m_signalSemaphore = sem;
 		}
 
 		hive.submitTasks(&dtasks[0], DEP_TASKS);
@@ -111,7 +114,7 @@ ANKI_TEST(Util, ThreadHive)
 		{
 			dtasks2[i].m_callback = taskToWait;
 			dtasks2[i].m_argument = &ctx;
-			dtasks2[i].m_inDependencies = WeakArray<ThreadHiveDependencyHandle>(&dtasks[i].m_outDependency, 1);
+			dtasks2[i].m_waitSemaphore = sem;
 		}
 
 		hive.submitTasks(&dtasks2[0], DEP_TASKS);
@@ -128,7 +131,7 @@ ANKI_TEST(Util, ThreadHive)
 		ctx.m_count = 0;
 
 		I number = 0;
-		ThreadHiveDependencyHandle dep = 0;
+		ThreadHiveSemaphore* sem = nullptr;
 
 		const U SUBMISSION_COUNT = 100;
 		const U TASK_COUNT = 1000;
@@ -143,21 +146,22 @@ ANKI_TEST(Util, ThreadHive)
 				ThreadHiveTask task;
 				task.m_callback = (cb) ? incNumber : decNumber;
 				task.m_argument = &ctx;
+				task.m_signalSemaphore = hive.newSemaphore(1);
 
-				if((rand() % 3) == 0 && j > 0 && dep)
+				if((rand() % 3) == 0 && j > 0 && sem)
 				{
-					task.m_inDependencies = WeakArray<ThreadHiveDependencyHandle>(&dep, 1);
+					task.m_waitSemaphore = sem;
 				}
 
 				hive.submitTasks(&task, 1);
 
 				if((rand() % 7) == 0)
 				{
-					dep = task.m_outDependency;
+					sem = task.m_signalSemaphore;
 				}
 			}
 
-			dep = 0;
+			sem = nullptr;
 			hive.waitAllTasks();
 		}
 
@@ -199,7 +203,7 @@ public:
 		}
 	}
 
-	static void callback(void* arg, U32, ThreadHive& hive)
+	static void callback(void* arg, U32, ThreadHive& hive, ThreadHiveSemaphore* sem)
 	{
 		static_cast<FibTask*>(arg)->doWork(hive);
 	}
@@ -223,7 +227,7 @@ ANKI_TEST(Util, ThreadHiveBench)
 
 	const U32 threadCount = getCpuCoresCount();
 	HeapAllocator<U8> alloc(allocAligned, nullptr);
-	ThreadHive hive(threadCount, alloc);
+	ThreadHive hive(threadCount, alloc, true);
 
 	StackAllocator<U8> salloc(allocAligned, nullptr, 1024);
 	Atomic<U64> sum = {0};