From cb699ef1995712bf4b8ddd0ad82845b811aa14ea Mon Sep 17 00:00:00 2001 From: assiduous Date: Thu, 24 Oct 2024 16:18:28 -0700 Subject: [PATCH] ThreadPool: added PinWorkerThread helper function --- Common/interface/ThreadPool.hpp | 12 +++++ Common/src/ThreadPool.cpp | 45 +++++++++++++++++++ .../Basic/interface/BasicPlatformMisc.hpp | 5 ++- Platforms/Basic/src/BasicPlatformMisc.cpp | 8 +++- .../Win32/interface/Win32PlatformMisc.hpp | 4 +- 5 files changed, 71 insertions(+), 3 deletions(-) diff --git a/Common/interface/ThreadPool.hpp b/Common/interface/ThreadPool.hpp index c19d75398..14088d4b1 100644 --- a/Common/interface/ThreadPool.hpp +++ b/Common/interface/ThreadPool.hpp @@ -62,6 +62,18 @@ struct ThreadPoolCreateInfo RefCntAutoPtr CreateThreadPool(const ThreadPoolCreateInfo& ThreadPoolCI); +/// Pins the worker thread to one of the allowed cores. +/// +/// \param ThreadId - The thread ID. +/// \param AllowedCoresMask - The bit mask of allowed cores. +/// \return - Previous thread affinity mask, or 0 if the function failed. +/// +/// \remarks The function selects the core by looping through the bits in the AllowedCoresMask. +/// For example, if cores 1, 3, 6 are allowed by the mask, the threads will be assigned +/// to cores 1, 3, 6, 1, 3, 6, etc. +/// +/// This function can be used as the OnThreadStarted callback in the ThreadPoolCreateInfo. +Uint64 PinWorkerThread(Uint32 ThreadId, Uint64 AllowedCoresMask); /// Base implementation of the IAsyncTask interface. class AsyncTaskBase : public ObjectBase diff --git a/Common/src/ThreadPool.cpp b/Common/src/ThreadPool.cpp index d556998c2..1b9b38b9d 100644 --- a/Common/src/ThreadPool.cpp +++ b/Common/src/ThreadPool.cpp @@ -34,6 +34,8 @@ #include #include +#include "PlatformMisc.hpp" + namespace Diligent { @@ -350,4 +352,47 @@ RefCntAutoPtr CreateThreadPool(const ThreadPoolCreateInfo& ThreadPo return RefCntAutoPtr{MakeNewRCObj()(ThreadPoolCI)}; } +Uint64 PinWorkerThread(Uint32 ThreadId, Uint64 AllowedCoresMask) +{ + if (AllowedCoresMask == 0) + { + return 0; + } + + Uint64 NumCores = std::thread::hardware_concurrency(); + if (NumCores <= 1) + return 0; + + Uint64 AffinityMask = AllowedCoresMask; + if (NumCores < 64) + AffinityMask &= (Uint64{1} << NumCores) - Uint64{1}; + + if (AffinityMask == 0) + { + LOG_WARNING_MESSAGE("Allowed cores mask (0x", std::hex, AllowedCoresMask, ") does not set any bits corresponding to ", std::dec, NumCores, " available cores"); + return 0; + } + + const Uint32 NumAllowedCores = PlatformMisc::CountOneBits(AffinityMask); + const Uint32 CoreBitInd = ThreadId % NumAllowedCores; + + for (Uint32 bit = 0; bit < CoreBitInd; ++bit) + { + VERIFY_EXPR(AffinityMask != 0); + Uint64 LSB = PlatformMisc::GetLSB(AffinityMask); + AffinityMask &= ~(Uint64{1} << LSB); + } + + VERIFY_EXPR(AffinityMask != 0); + Uint32 WorkerCore = PlatformMisc::GetLSB(AffinityMask); + VERIFY_EXPR(WorkerCore < NumCores); + Uint64 PrevMask = PlatformMisc::SetCurrentThreadAffinity(Uint64{1} << WorkerCore) != 0; + if (PrevMask == 0) + { + LOG_WARNING_MESSAGE("Failed to pin worker thread ", ThreadId, " to core ", WorkerCore); + } + + return PrevMask; +} + } // namespace Diligent diff --git a/Platforms/Basic/interface/BasicPlatformMisc.hpp b/Platforms/Basic/interface/BasicPlatformMisc.hpp index c7ac2ae0e..76ce0a611 100644 --- a/Platforms/Basic/interface/BasicPlatformMisc.hpp +++ b/Platforms/Basic/interface/BasicPlatformMisc.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Diligent Graphics LLC + * Copyright 2019-2024 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -107,6 +107,9 @@ struct BasicPlatformMisc /// On failure, returns ThreadPriority::Unknown. static ThreadPriority SetCurrentThreadPriority(ThreadPriority Priority); + /// Sets the current thread affinity mask and on success returns the previous mask. + static Uint64 SetCurrentThreadAffinity(Uint64 Mask); + private: static void SwapBytes16(Uint16& Val) { diff --git a/Platforms/Basic/src/BasicPlatformMisc.cpp b/Platforms/Basic/src/BasicPlatformMisc.cpp index 5bc855711..90dd8f8d2 100644 --- a/Platforms/Basic/src/BasicPlatformMisc.cpp +++ b/Platforms/Basic/src/BasicPlatformMisc.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Diligent Graphics LLC + * Copyright 2019-2024 Diligent Graphics LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,4 +42,10 @@ ThreadPriority BasicPlatformMisc::SetCurrentThreadPriority(ThreadPriority Priori return ThreadPriority::Unknown; } +Uint64 BasicPlatformMisc::SetCurrentThreadAffinity(Uint64 Mask) +{ + LOG_WARNING_MESSAGE_ONCE("SetCurrentThreadAffinity is not implemented on this platform."); + return 0; +} + } // namespace Diligent diff --git a/Platforms/Win32/interface/Win32PlatformMisc.hpp b/Platforms/Win32/interface/Win32PlatformMisc.hpp index de5b5ed38..47ea4fa63 100644 --- a/Platforms/Win32/interface/Win32PlatformMisc.hpp +++ b/Platforms/Win32/interface/Win32PlatformMisc.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 Diligent Graphics LLC + * Copyright 2019-2024 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -163,6 +163,7 @@ struct WindowsMisc : public BasicPlatformMisc return reinterpret_cast(SwappedBytes); } +#if PLATFORM_WIN32 /// Sets the current thread affinity mask and on success returns the previous mask. /// On failure, returns 0. static Uint64 SetCurrentThreadAffinity(Uint64 Mask); @@ -172,6 +173,7 @@ struct WindowsMisc : public BasicPlatformMisc /// Sets the current thread priority and on success returns the previous priority. /// On failure, returns ThreadPriority::Unknown. static ThreadPriority SetCurrentThreadPriority(ThreadPriority Priority); +#endif }; } // namespace Diligent