This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/blenlib/BLI_task.hh
Jacques Lucke 5c81d3bd46 Geometry Nodes: improve evaluator with lazy threading
In large node setup the threading overhead was sometimes very significant.
That's especially true when most nodes do very little work.

This commit improves the scheduling by not using multi-threading in many
cases unless it's likely that it will be worth it. For more details see the comments
in `BLI_lazy_threading.hh`.

Differential Revision: https://developer.blender.org/D15976
2022-09-20 11:08:05 +02:00

139 lines
3.8 KiB
C++

/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bli
*/
#ifdef WITH_TBB
/* Quiet top level deprecation message, unrelated to API usage here. */
# if defined(WIN32) && !defined(NOMINMAX)
/* TBB includes Windows.h which will define min/max macros causing issues
* when we try to use std::min and std::max later on. */
# define NOMINMAX
# define TBB_MIN_MAX_CLEANUP
# endif
# include <tbb/blocked_range.h>
# include <tbb/parallel_for.h>
# include <tbb/parallel_for_each.h>
# include <tbb/parallel_invoke.h>
# include <tbb/parallel_reduce.h>
# include <tbb/task_arena.h>
# ifdef WIN32
/* We cannot keep this defined, since other parts of the code deal with this on their own, leading
* to multiple define warnings unless we un-define this, however we can only undefine this if we
* were the ones that made the definition earlier. */
# ifdef TBB_MIN_MAX_CLEANUP
# undef NOMINMAX
# endif
# endif
#endif
#include "BLI_index_range.hh"
#include "BLI_lazy_threading.hh"
#include "BLI_utildefines.h"
namespace blender::threading {
template<typename Range, typename Function>
void parallel_for_each(Range &range, const Function &function)
{
#ifdef WITH_TBB
tbb::parallel_for_each(range, function);
#else
for (auto &value : range) {
function(value);
}
#endif
}
template<typename Function>
void parallel_for(IndexRange range, int64_t grain_size, const Function &function)
{
if (range.size() == 0) {
return;
}
#ifdef WITH_TBB
/* Invoking tbb for small workloads has a large overhead. */
if (range.size() >= grain_size) {
lazy_threading::send_hint();
tbb::parallel_for(
tbb::blocked_range<int64_t>(range.first(), range.one_after_last(), grain_size),
[&](const tbb::blocked_range<int64_t> &subrange) {
function(IndexRange(subrange.begin(), subrange.size()));
});
return;
}
#else
UNUSED_VARS(grain_size);
#endif
function(range);
}
template<typename Value, typename Function, typename Reduction>
Value parallel_reduce(IndexRange range,
int64_t grain_size,
const Value &identity,
const Function &function,
const Reduction &reduction)
{
#ifdef WITH_TBB
if (range.size() >= grain_size) {
lazy_threading::send_hint();
return tbb::parallel_reduce(
tbb::blocked_range<int64_t>(range.first(), range.one_after_last(), grain_size),
identity,
[&](const tbb::blocked_range<int64_t> &subrange, const Value &ident) {
return function(IndexRange(subrange.begin(), subrange.size()), ident);
},
reduction);
}
#else
UNUSED_VARS(grain_size, reduction);
#endif
return function(range, identity);
}
/**
* Execute all of the provided functions. The functions might be executed in parallel or in serial
* or some combination of both.
*/
template<typename... Functions> void parallel_invoke(Functions &&...functions)
{
#ifdef WITH_TBB
tbb::parallel_invoke(std::forward<Functions>(functions)...);
#else
(functions(), ...);
#endif
}
/**
* Same #parallel_invoke, but allows disabling threading dynamically. This is useful because when
* the individual functions do very little work, there is a lot of overhead from starting parallel
* tasks.
*/
template<typename... Functions>
void parallel_invoke(const bool use_threading, Functions &&...functions)
{
if (use_threading) {
lazy_threading::send_hint();
parallel_invoke(std::forward<Functions>(functions)...);
}
else {
(functions(), ...);
}
}
/** See #BLI_task_isolate for a description of what isolating a task means. */
template<typename Function> void isolate_task(const Function &function)
{
#ifdef WITH_TBB
tbb::this_task_arena::isolate(function);
#else
function();
#endif
}
} // namespace blender::threading