Skip to content

core: Add semantic ROOT::EnableImplicitMT #18694

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 7 commits into from
May 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions core/base/inc/TROOT.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,20 @@ namespace Internal {
} } // End ROOT::Internal

namespace ROOT {
enum class EIMTConfig {
kWholeMachine = 0, ///< Default configuration
kExistingTBBArena = 1, ///< Use the existing TBB arena
kNumConfigs = 2 ///< Number of support IMT semantic configurations
};
/// \brief Enable support for multi-threading within the ROOT code
/// in particular, enables the global mutex to make ROOT thread safe/aware.
void EnableThreadSafety();
/// \brief Enable ROOT's implicit multi-threading for all objects and methods that provide an internal
/// parallelisation mechanism.
void EnableImplicitMT(UInt_t numthreads = 0);
/// \brief Enable ROOT's implicit multi-threading for all objects and methods that provide an internal
/// parallelisation mechanism.
void EnableImplicitMT(ROOT::EIMTConfig config);
void DisableImplicitMT();
Bool_t IsImplicitMTEnabled();
UInt_t GetThreadPoolSize();
Expand Down
25 changes: 25 additions & 0 deletions core/base/src/TROOT.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,31 @@ namespace Internal {
#endif
}

////////////////////////////////////////////////////////////////////////////////
/// @param[in] config Configuration to use. The default is kWholeMachine, which
/// will create a thread pool that spans the whole machine.
///
/// EnableImplicitMT calls in turn EnableThreadSafety.
/// If ImplicitMT is already enabled, this function does nothing.

void EnableImplicitMT(ROOT::EIMTConfig config)
{
#ifdef R__USE_IMT
if (ROOT::Internal::IsImplicitMTEnabledImpl())
return;
EnableThreadSafety();
static void (*sym)(ROOT::EIMTConfig) =
(void (*)(ROOT::EIMTConfig))Internal::GetSymInLibImt("ROOT_TImplicitMT_EnableImplicitMT_Config");
if (sym)
sym(config);
ROOT::Internal::IsImplicitMTEnabledImpl() = true;
#else
::Warning("EnableImplicitMT",
"Cannot enable implicit multi-threading with config %d, please build ROOT with -Dimt=ON",
static_cast<int>(config));
#endif
}

////////////////////////////////////////////////////////////////////////////////
/// Disables the implicit multi-threading in ROOT (see EnableImplicitMT).
void DisableImplicitMT()
Expand Down
10 changes: 8 additions & 2 deletions core/imt/inc/ROOT/RTaskArena.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#define ROOT_RTaskArena

#include "RConfigure.h"
#include "TROOT.h" // For ROOT::EIMTConfig
#include <memory>

// exclude in case ROOT does not have IMT support
Expand Down Expand Up @@ -65,9 +66,13 @@ public:
~RTaskArenaWrapper(); // necessary to set size back to zero
static unsigned TaskArenaSize(); // A static getter lets us check for RTaskArenaWrapper's existence
ROOT::ROpaqueTaskArena &Access();
private:
struct Attach {}; ///< Marker for attaching to an existing tbb::task_arena

RTaskArenaWrapper(unsigned maxConcurrency = 0);
friend std::shared_ptr<ROOT::Internal::RTaskArenaWrapper> GetGlobalTaskArena(unsigned maxConcurrency);
RTaskArenaWrapper(Attach);

private:
friend std::shared_ptr<ROOT::Internal::RTaskArenaWrapper> GetGlobalTaskArena(unsigned, ROOT::EIMTConfig);
std::unique_ptr<ROOT::ROpaqueTaskArena> fTBBArena;
static unsigned fNWorkers;
};
Expand All @@ -81,6 +86,7 @@ private:
/// references to the previous one are gone and the object destroyed.
////////////////////////////////////////////////////////////////////////////////
std::shared_ptr<ROOT::Internal::RTaskArenaWrapper> GetGlobalTaskArena(unsigned maxConcurrency = 0);
std::shared_ptr<ROOT::Internal::RTaskArenaWrapper> GetGlobalTaskArena(ROOT::EIMTConfig config);

} // namespace Internal
} // namespace ROOT
Expand Down
5 changes: 4 additions & 1 deletion core/imt/src/ROpaqueTaskArena.hxx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#include "tbb/task_arena.h"

namespace ROOT {
class ROpaqueTaskArena: public tbb::task_arena {};
class ROpaqueTaskArena : public tbb::task_arena {
public:
using tbb::task_arena::task_arena;
};
}
40 changes: 38 additions & 2 deletions core/imt/src/RTaskArena.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,20 @@ RTaskArenaWrapper::RTaskArenaWrapper(unsigned maxConcurrency) : fTBBArena(new RO
ROOT::EnableThreadSafety();
}

////////////////////////////////////////////////////////////////////////////////
/// Initializes the tbb::task_arena within RTaskArenaWrapper by attaching to an
/// existing arena.
///
/// * Can't be reinitialized
////////////////////////////////////////////////////////////////////////////////
RTaskArenaWrapper::RTaskArenaWrapper(RTaskArenaWrapper::Attach)
: fTBBArena(new ROpaqueTaskArena{tbb::task_arena::attach{}})
{
fTBBArena->initialize(tbb::task_arena::attach{});
fNWorkers = fTBBArena->max_concurrency();
ROOT::EnableThreadSafety();
}

RTaskArenaWrapper::~RTaskArenaWrapper()
{
fNWorkers = 0u;
Expand All @@ -127,7 +141,8 @@ ROOT::ROpaqueTaskArena &RTaskArenaWrapper::Access()
return *fTBBArena;
}

std::shared_ptr<ROOT::Internal::RTaskArenaWrapper> GetGlobalTaskArena(unsigned maxConcurrency)
std::shared_ptr<ROOT::Internal::RTaskArenaWrapper>
GetGlobalTaskArena(unsigned maxConcurrency, ROOT::EIMTConfig config)
{
static std::weak_ptr<ROOT::Internal::RTaskArenaWrapper> weak_GTAWrapper;

Expand All @@ -140,10 +155,31 @@ std::shared_ptr<ROOT::Internal::RTaskArenaWrapper> GetGlobalTaskArena(unsigned m
}
return sp;
}
std::shared_ptr<ROOT::Internal::RTaskArenaWrapper> sp(new ROOT::Internal::RTaskArenaWrapper(maxConcurrency));
std::shared_ptr<ROOT::Internal::RTaskArenaWrapper> sp;
if (config == ROOT::EIMTConfig::kExistingTBBArena) {
sp = std::make_shared<ROOT::Internal::RTaskArenaWrapper>(ROOT::Internal::RTaskArenaWrapper::Attach{});
} else {
if (config == ROOT::EIMTConfig::kWholeMachine) {
maxConcurrency = 0;
}
sp = std::make_shared<ROOT::Internal::RTaskArenaWrapper>(maxConcurrency);
}
weak_GTAWrapper = sp;
return sp;
}

std::shared_ptr<ROOT::Internal::RTaskArenaWrapper> GetGlobalTaskArena(ROOT::EIMTConfig config)
{
if (config >= ROOT::EIMTConfig::kNumConfigs)
::Fatal("ROOT::Internal::GetGlobalTaskArena",
"Unsupported enum value %d", (int)config);
return GetGlobalTaskArena(0, config);
}

std::shared_ptr<ROOT::Internal::RTaskArenaWrapper> GetGlobalTaskArena(unsigned maxConcurrency)
{
return GetGlobalTaskArena(maxConcurrency, ROOT::EIMTConfig::kNumConfigs);
}

} // namespace Internal
} // namespace ROOT
17 changes: 17 additions & 0 deletions core/imt/src/TImplicitMT.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
// //
//////////////////////////////////////////////////////////////////////////

#include "TROOT.h"
#include "TError.h"
#include "ROOT/RTaskArena.hxx"
#include <atomic>
Expand Down Expand Up @@ -55,6 +56,22 @@ extern "C" void ROOT_TImplicitMT_EnableImplicitMT(UInt_t numthreads)
}
};

extern "C" void ROOT_TImplicitMT_EnableImplicitMT_Config(ROOT::EIMTConfig config)
{
if (!GetImplicitMTFlag()) {
if (config < ROOT::EIMTConfig::kNumConfigs) {
R__GetTaskArena4IMT() = ROOT::Internal::GetGlobalTaskArena(config);
} else {
::Warning("ROOT_TImplicitMT_EnableImplicitMT_Config",
"Unknown enum value %d defaulting to EIMTCconfig::kWholeMachine", (int)config);
R__GetTaskArena4IMT() = ROOT::Internal::GetGlobalTaskArena(0);
}
GetImplicitMTFlag() = true;
} else {
::Warning("ROOT_TImplicitMT_EnableImplicitMT_Config", "Implicit multi-threading is already enabled");
}
};

extern "C" void ROOT_TImplicitMT_DisableImplicitMT()
{
if (GetImplicitMTFlag()) {
Expand Down
1 change: 1 addition & 0 deletions core/imt/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@

ROOT_ADD_GTEST(testTaskArena testRTaskArena.cxx LIBRARIES Imt ${TBB_LIBRARIES} FAILREGEX "")
ROOT_ADD_GTEST(testTBBGlobalControl testTBBGlobalControl.cxx LIBRARIES Imt ${TBB_LIBRARIES})
ROOT_ADD_GTEST(testEnableImt testEnableImt.cxx LIBRARIES Imt ${TBB_LIBRARIES})
26 changes: 26 additions & 0 deletions core/imt/test/testEnableImt.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include "TROOT.h"
#include "ROOT/RTaskArena.hxx"

#include "tbb/task_arena.h"

#include "ROOT/TestSupport.hxx"
#include "gtest/gtest.h"

#ifdef R__USE_IMT

static const unsigned gMaxConcurrency = ROOT::Internal::LogicalCPUBandwidthControl();

TEST(EnableImt, TBBAttach)
{
tbb::task_arena main_arena{2};

main_arena.execute([&]() { ROOT::EnableImplicitMT(ROOT::EIMTConfig::kExistingTBBArena); });

auto psize = ROOT::GetThreadPoolSize();

EXPECT_TRUE(psize > 1);
EXPECT_EQ(main_arena.max_concurrency(), 2);
EXPECT_EQ(psize, 2);
}

#endif
Loading