X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fmisc.cpp;h=7075dd3cdc8498192cd8f256740fcc497cb63827;hp=08df524bf26beb5bed4453910cc161b1ed8364a3;hb=0d9a9f5e985c13852cf9f29767e95f295bb29575;hpb=0d669be76cadd33c801ba9e07410c789d2c7b364 diff --git a/src/misc.cpp b/src/misc.cpp index 08df524b..7075dd3c 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -18,10 +18,19 @@ along with this program. If not, see . */ +#ifdef _WIN32 +#if _WIN32_WINNT < 0x0601 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0601 // Force to include newest API (Win 7 or later) +#endif +#include // For processor groups +#endif + #include #include #include #include +#include #include "misc.h" #include "thread.h" @@ -185,3 +194,101 @@ void prefetch(void* addr) { } #endif + +namespace WinProcGroup { + +#ifndef _WIN32 + +void bindThisThread(size_t) {} + +#else + +/// get_group() retrieves logical processor information using Windows specific +/// API and returns the best group id for the thread with index idx. Original +/// code from Texel by Peter Österlund. + +int get_group(size_t idx) { + + int threads = 0; + int nodes = 0; + int cores = 0; + DWORD returnLength = 0; + DWORD byteOffset = 0; + + // Early exit if the needed API are not available at runtime + HMODULE k32 = GetModuleHandle("Kernel32.dll"); + if ( !GetProcAddress(k32, "GetLogicalProcessorInformationEx") + || !GetProcAddress(k32, "GetNumaNodeProcessorMaskEx") + || !GetProcAddress(k32, "SetThreadGroupAffinity")) + return -1; + + // First call to get returnLength. We expect it to fail due to null buffer + if (GetLogicalProcessorInformationEx(RelationAll, nullptr, &returnLength)) + return -1; + + // Once we know returnLength, allocate the buffer + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *buffer, *ptr; + ptr = buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(returnLength); + + // Second call, now we expect to succeed + if (!GetLogicalProcessorInformationEx(RelationAll, buffer, &returnLength)) + { + free(buffer); + return -1; + } + + while (ptr->Size > 0 && byteOffset + ptr->Size <= returnLength) + { + if (ptr->Relationship == RelationNumaNode) + nodes++; + + else if (ptr->Relationship == RelationProcessorCore) + { + cores++; + threads += (ptr->Processor.Flags == LTP_PC_SMT) ? 2 : 1; + } + + byteOffset += ptr->Size; + ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)(((char*)ptr) + ptr->Size); + } + + free(buffer); + + std::vector groups; + + // Run as many threads as possible on the same node until core limit is + // reached, then move on filling the next node. + for (int n = 0; n < nodes; n++) + for (int i = 0; i < cores / nodes; i++) + groups.push_back(n); + + // In case a core has more than one logical processor (we assume 2) and we + // have still threads to allocate, then spread them evenly across available + // nodes. + for (int t = 0; t < threads - cores; t++) + groups.push_back(t % nodes); + + // If we still have more threads than the total number of logical processors + // then return -1 and let the OS to decide what to do. + return idx < groups.size() ? groups[idx] : -1; +} + + +/// bindThisThread() set the group affinity of the current thread + +void bindThisThread(size_t idx) { + + // Use a local variable instead of a static: slower but thread-safe + int group = get_group(idx); + + if (group == -1) + return; + + GROUP_AFFINITY mask; + if (GetNumaNodeProcessorMaskEx(group, &mask)) + SetThreadGroupAffinity(GetCurrentThread(), &mask, nullptr); +} + +#endif + +} // namespace WinProcGroup