| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #ifdef _WIN32 |
| #if _WIN32_WINNT < 0x0601 |
| #undef _WIN32_WINNT |
| #define _WIN32_WINNT 0x0601 |
| #endif |
|
|
| #ifndef NOMINMAX |
| #define NOMINMAX |
| #endif |
|
|
| #include <windows.h> |
| |
| |
| |
| |
| extern "C" { |
| typedef bool(*fun1_t)(LOGICAL_PROCESSOR_RELATIONSHIP, |
| PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PDWORD); |
| typedef bool(*fun2_t)(USHORT, PGROUP_AFFINITY); |
| typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); |
| typedef bool(*fun4_t)(USHORT, PGROUP_AFFINITY, USHORT, PUSHORT); |
| typedef WORD(*fun5_t)(); |
| } |
| #endif |
|
|
| #include <fstream> |
| #include <iomanip> |
| #include <iostream> |
| #include <sstream> |
| #include <vector> |
| #include <cstdlib> |
|
|
| #if defined(__linux__) && !defined(__ANDROID__) |
| #include <stdlib.h> |
| #include <sys/mman.h> |
| #endif |
|
|
| #if defined(__APPLE__) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32)) || defined(__e2k__) |
| #define POSIXALIGNEDALLOC |
| #include <stdlib.h> |
| #endif |
|
|
| #include "misc.h" |
| #include "thread.h" |
|
|
| using namespace std; |
|
|
| namespace Stockfish { |
|
|
| namespace { |
|
|
| |
| const string version = "15.1"; |
|
|
| |
| |
| |
| |
| |
|
|
| struct Tie: public streambuf { |
|
|
| Tie(streambuf* b, streambuf* l) : buf(b), logBuf(l) {} |
|
|
| int sync() override { return logBuf->pubsync(), buf->pubsync(); } |
| int overflow(int c) override { return log(buf->sputc((char)c), "<< "); } |
| int underflow() override { return buf->sgetc(); } |
| int uflow() override { return log(buf->sbumpc(), ">> "); } |
|
|
| streambuf *buf, *logBuf; |
|
|
| int log(int c, const char* prefix) { |
|
|
| static int last = '\n'; |
|
|
| if (last == '\n') |
| logBuf->sputn(prefix, 3); |
|
|
| return last = logBuf->sputc((char)c); |
| } |
| }; |
|
|
| class Logger { |
|
|
| Logger() : in(cin.rdbuf(), file.rdbuf()), out(cout.rdbuf(), file.rdbuf()) {} |
| ~Logger() { start(""); } |
|
|
| ofstream file; |
| Tie in, out; |
|
|
| public: |
| static void start(const std::string& fname) { |
|
|
| static Logger l; |
|
|
| if (l.file.is_open()) |
| { |
| cout.rdbuf(l.out.buf); |
| cin.rdbuf(l.in.buf); |
| l.file.close(); |
| } |
|
|
| if (!fname.empty()) |
| { |
| l.file.open(fname, ifstream::out); |
|
|
| if (!l.file.is_open()) |
| { |
| cerr << "Unable to open debug log file " << fname << endl; |
| exit(EXIT_FAILURE); |
| } |
|
|
| cin.rdbuf(&l.in); |
| cout.rdbuf(&l.out); |
| } |
| } |
| }; |
|
|
| } |
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| string engine_info(bool to_uci) { |
| stringstream ss; |
| ss << "Stockfish " << version << setfill('0'); |
|
|
| if (version == "dev") |
| { |
| ss << "-"; |
| #ifdef GIT_DATE |
| ss << GIT_DATE; |
| #else |
| const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); |
| string month, day, year; |
| stringstream date(__DATE__); |
|
|
| date >> month >> day >> year; |
| ss << year << setw(2) << setfill('0') << (1 + months.find(month) / 4) << setw(2) << setfill('0') << day; |
| #endif |
|
|
| ss << "-"; |
|
|
| #ifdef GIT_SHA |
| ss << GIT_SHA; |
| #else |
| ss << "nogit"; |
| #endif |
| } |
|
|
| ss << (to_uci ? "\nid author ": " by ") |
| << "the Stockfish developers (see AUTHORS file)"; |
|
|
| return ss.str(); |
| } |
|
|
|
|
| |
|
|
| std::string compiler_info() { |
|
|
| #define stringify2(x) #x |
| #define stringify(x) stringify2(x) |
| #define make_version_string(major, minor, patch) stringify(major) "." stringify(minor) "." stringify(patch) |
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| std::string compiler = "\nCompiled by "; |
|
|
| #ifdef __clang__ |
| compiler += "clang++ "; |
| compiler += make_version_string(__clang_major__, __clang_minor__, __clang_patchlevel__); |
| #elif __INTEL_COMPILER |
| compiler += "Intel compiler "; |
| compiler += "(version "; |
| compiler += stringify(__INTEL_COMPILER) " update " stringify(__INTEL_COMPILER_UPDATE); |
| compiler += ")"; |
| #elif _MSC_VER |
| compiler += "MSVC "; |
| compiler += "(version "; |
| compiler += stringify(_MSC_FULL_VER) "." stringify(_MSC_BUILD); |
| compiler += ")"; |
| #elif defined(__e2k__) && defined(__LCC__) |
| #define dot_ver2(n) \ |
| compiler += (char)'.'; \ |
| compiler += (char)('0' + (n) / 10); \ |
| compiler += (char)('0' + (n) % 10); |
|
|
| compiler += "MCST LCC "; |
| compiler += "(version "; |
| compiler += std::to_string(__LCC__ / 100); |
| dot_ver2(__LCC__ % 100) |
| dot_ver2(__LCC_MINOR__) |
| compiler += ")"; |
| #elif __GNUC__ |
| compiler += "g++ (GNUC) "; |
| compiler += make_version_string(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); |
| #else |
| compiler += "Unknown compiler "; |
| compiler += "(unknown version)"; |
| #endif |
|
|
| #if defined(__APPLE__) |
| compiler += " on Apple"; |
| #elif defined(__CYGWIN__) |
| compiler += " on Cygwin"; |
| #elif defined(__MINGW64__) |
| compiler += " on MinGW64"; |
| #elif defined(__MINGW32__) |
| compiler += " on MinGW32"; |
| #elif defined(__ANDROID__) |
| compiler += " on Android"; |
| #elif defined(__linux__) |
| compiler += " on Linux"; |
| #elif defined(_WIN64) |
| compiler += " on Microsoft Windows 64-bit"; |
| #elif defined(_WIN32) |
| compiler += " on Microsoft Windows 32-bit"; |
| #else |
| compiler += " on unknown system"; |
| #endif |
|
|
| compiler += "\nCompilation settings include: "; |
| compiler += (Is64Bit ? " 64bit" : " 32bit"); |
| #if defined(USE_VNNI) |
| compiler += " VNNI"; |
| #endif |
| #if defined(USE_AVX512) |
| compiler += " AVX512"; |
| #endif |
| compiler += (HasPext ? " BMI2" : ""); |
| #if defined(USE_AVX2) |
| compiler += " AVX2"; |
| #endif |
| #if defined(USE_SSE41) |
| compiler += " SSE41"; |
| #endif |
| #if defined(USE_SSSE3) |
| compiler += " SSSE3"; |
| #endif |
| #if defined(USE_SSE2) |
| compiler += " SSE2"; |
| #endif |
| compiler += (HasPopCnt ? " POPCNT" : ""); |
| #if defined(USE_MMX) |
| compiler += " MMX"; |
| #endif |
| #if defined(USE_NEON) |
| compiler += " NEON"; |
| #endif |
|
|
| #if !defined(NDEBUG) |
| compiler += " DEBUG"; |
| #endif |
|
|
| compiler += "\n__VERSION__ macro expands to: "; |
| #ifdef __VERSION__ |
| compiler += __VERSION__; |
| #else |
| compiler += "(undefined macro)"; |
| #endif |
| compiler += "\n"; |
|
|
| return compiler; |
| } |
|
|
|
|
| |
| static std::atomic<int64_t> hits[2], means[2]; |
|
|
| void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; } |
| void dbg_hit_on(bool c, bool b) { if (c) dbg_hit_on(b); } |
| void dbg_mean_of(int v) { ++means[0]; means[1] += v; } |
|
|
| void dbg_print() { |
|
|
| if (hits[0]) |
| cerr << "Total " << hits[0] << " Hits " << hits[1] |
| << " hit rate (%) " << 100 * hits[1] / hits[0] << endl; |
|
|
| if (means[0]) |
| cerr << "Total " << means[0] << " Mean " |
| << (double)means[1] / means[0] << endl; |
| } |
|
|
|
|
| |
| |
|
|
| std::ostream& operator<<(std::ostream& os, SyncCout sc) { |
|
|
| static std::mutex m; |
|
|
| if (sc == IO_LOCK) |
| m.lock(); |
|
|
| if (sc == IO_UNLOCK) |
| m.unlock(); |
|
|
| return os; |
| } |
|
|
|
|
| |
| void start_logger(const std::string& fname) { Logger::start(fname); } |
|
|
|
|
| |
| |
| |
| #ifdef NO_PREFETCH |
|
|
| void prefetch(void*) {} |
|
|
| #else |
|
|
| void prefetch(void* addr) { |
|
|
| # if defined(__INTEL_COMPILER) |
| |
| |
| __asm__ (""); |
| # endif |
|
|
| # if defined(__INTEL_COMPILER) || defined(_MSC_VER) |
| _mm_prefetch((char*)addr, _MM_HINT_T0); |
| # else |
| __builtin_prefetch(addr); |
| # endif |
| } |
|
|
| #endif |
|
|
|
|
| |
| |
| |
|
|
| void* std_aligned_alloc(size_t alignment, size_t size) { |
|
|
| #if defined(POSIXALIGNEDALLOC) |
| void *mem; |
| return posix_memalign(&mem, alignment, size) ? nullptr : mem; |
| #elif defined(_WIN32) |
| return _mm_malloc(size, alignment); |
| #else |
| return std::aligned_alloc(alignment, size); |
| #endif |
| } |
|
|
| void std_aligned_free(void* ptr) { |
|
|
| #if defined(POSIXALIGNEDALLOC) |
| free(ptr); |
| #elif defined(_WIN32) |
| _mm_free(ptr); |
| #else |
| free(ptr); |
| #endif |
| } |
|
|
| |
|
|
| #if defined(_WIN32) |
|
|
| static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize) { |
|
|
| #if !defined(_WIN64) |
| return nullptr; |
| #else |
|
|
| HANDLE hProcessToken { }; |
| LUID luid { }; |
| void* mem = nullptr; |
|
|
| const size_t largePageSize = GetLargePageMinimum(); |
| if (!largePageSize) |
| return nullptr; |
|
|
| |
| if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken)) |
| return nullptr; |
|
|
| if (LookupPrivilegeValue(NULL, SE_LOCK_MEMORY_NAME, &luid)) |
| { |
| TOKEN_PRIVILEGES tp { }; |
| TOKEN_PRIVILEGES prevTp { }; |
| DWORD prevTpLen = 0; |
|
|
| tp.PrivilegeCount = 1; |
| tp.Privileges[0].Luid = luid; |
| tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; |
|
|
| |
| |
| if (AdjustTokenPrivileges( |
| hProcessToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevTp, &prevTpLen) && |
| GetLastError() == ERROR_SUCCESS) |
| { |
| |
| allocSize = (allocSize + largePageSize - 1) & ~size_t(largePageSize - 1); |
| mem = VirtualAlloc( |
| NULL, allocSize, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); |
|
|
| |
| AdjustTokenPrivileges(hProcessToken, FALSE, &prevTp, 0, NULL, NULL); |
| } |
| } |
|
|
| CloseHandle(hProcessToken); |
|
|
| return mem; |
|
|
| #endif |
| } |
|
|
| void* aligned_large_pages_alloc(size_t allocSize) { |
|
|
| |
| void* mem = aligned_large_pages_alloc_windows(allocSize); |
|
|
| |
| if (!mem) |
| mem = VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); |
|
|
| return mem; |
| } |
|
|
| #else |
|
|
| void* aligned_large_pages_alloc(size_t allocSize) { |
|
|
| #if defined(__linux__) |
| constexpr size_t alignment = 2 * 1024 * 1024; |
| #else |
| constexpr size_t alignment = 4096; |
| #endif |
|
|
| |
| size_t size = ((allocSize + alignment - 1) / alignment) * alignment; |
| void *mem = std_aligned_alloc(alignment, size); |
| #if defined(MADV_HUGEPAGE) |
| madvise(mem, size, MADV_HUGEPAGE); |
| #endif |
| return mem; |
| } |
|
|
| #endif |
|
|
|
|
| |
|
|
| #if defined(_WIN32) |
|
|
| void aligned_large_pages_free(void* mem) { |
|
|
| if (mem && !VirtualFree(mem, 0, MEM_RELEASE)) |
| { |
| DWORD err = GetLastError(); |
| std::cerr << "Failed to free large page memory. Error code: 0x" |
| << std::hex << err |
| << std::dec << std::endl; |
| exit(EXIT_FAILURE); |
| } |
| } |
|
|
| #else |
|
|
| void aligned_large_pages_free(void *mem) { |
| std_aligned_free(mem); |
| } |
|
|
| #endif |
|
|
|
|
| namespace WinProcGroup { |
|
|
| #ifndef _WIN32 |
|
|
| void bindThisThread(size_t) {} |
|
|
| #else |
|
|
| |
| |
| |
|
|
| int best_node(size_t idx) { |
|
|
| int threads = 0; |
| int nodes = 0; |
| int cores = 0; |
| DWORD returnLength = 0; |
| DWORD byteOffset = 0; |
|
|
| |
| HMODULE k32 = GetModuleHandle("Kernel32.dll"); |
| auto fun1 = (fun1_t)(void(*)())GetProcAddress(k32, "GetLogicalProcessorInformationEx"); |
| if (!fun1) |
| return -1; |
|
|
| |
| |
| if (fun1(RelationAll, nullptr, &returnLength)) |
| return -1; |
|
|
| |
| SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *buffer, *ptr; |
| ptr = buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(returnLength); |
|
|
| |
| if (!fun1(RelationAll, buffer, &returnLength)) |
| { |
| free(buffer); |
| return -1; |
| } |
|
|
| while (byteOffset < returnLength) |
| { |
| if (ptr->Relationship == RelationNumaNode) |
| nodes++; |
|
|
| else if (ptr->Relationship == RelationProcessorCore) |
| { |
| cores++; |
| threads += (ptr->Processor.Flags == LTP_PC_SMT) ? 2 : 1; |
| } |
|
|
| assert(ptr->Size); |
| byteOffset += ptr->Size; |
| ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)(((char*)ptr) + ptr->Size); |
| } |
|
|
| free(buffer); |
|
|
| std::vector<int> groups; |
|
|
| |
| |
| for (int n = 0; n < nodes; n++) |
| for (int i = 0; i < cores / nodes; i++) |
| groups.push_back(n); |
|
|
| |
| |
| |
| for (int t = 0; t < threads - cores; t++) |
| groups.push_back(t % nodes); |
|
|
| |
| |
| return idx < groups.size() ? groups[idx] : -1; |
| } |
|
|
|
|
| |
|
|
| void bindThisThread(size_t idx) { |
|
|
| |
| int node = best_node(idx); |
|
|
| if (node == -1) |
| return; |
|
|
| |
| HMODULE k32 = GetModuleHandle("Kernel32.dll"); |
| auto fun2 = (fun2_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMaskEx"); |
| auto fun3 = (fun3_t)(void(*)())GetProcAddress(k32, "SetThreadGroupAffinity"); |
| auto fun4 = (fun4_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMask2"); |
| auto fun5 = (fun5_t)(void(*)())GetProcAddress(k32, "GetMaximumProcessorGroupCount"); |
|
|
| if (!fun2 || !fun3) |
| return; |
|
|
| if (!fun4 || !fun5) |
| { |
| GROUP_AFFINITY affinity; |
| if (fun2(node, &affinity)) |
| fun3(GetCurrentThread(), &affinity, nullptr); |
| } |
| else |
| { |
| |
| |
| USHORT elements, returnedElements; |
| elements = fun5(); |
| GROUP_AFFINITY *affinity = (GROUP_AFFINITY*)malloc(elements * sizeof(GROUP_AFFINITY)); |
| if (fun4(node, affinity, elements, &returnedElements)) |
| fun3(GetCurrentThread(), &affinity[idx % returnedElements], nullptr); |
| free(affinity); |
| } |
| } |
|
|
| #endif |
|
|
| } |
|
|
| #ifdef _WIN32 |
| #include <direct.h> |
| #define GETCWD _getcwd |
| #else |
| #include <unistd.h> |
| #define GETCWD getcwd |
| #endif |
|
|
| namespace CommandLine { |
|
|
| string argv0; |
| string binaryDirectory; |
| string workingDirectory; |
|
|
| void init([[maybe_unused]] int argc, char* argv[]) { |
| string pathSeparator; |
|
|
| |
| argv0 = argv[0]; |
|
|
| #ifdef _WIN32 |
| pathSeparator = "\\"; |
| #ifdef _MSC_VER |
| |
| |
| char* pgmptr = nullptr; |
| if (!_get_pgmptr(&pgmptr) && pgmptr != nullptr && *pgmptr) |
| argv0 = pgmptr; |
| #endif |
| #else |
| pathSeparator = "/"; |
| #endif |
|
|
| |
| workingDirectory = ""; |
| char buff[40000]; |
| char* cwd = GETCWD(buff, 40000); |
| if (cwd) |
| workingDirectory = cwd; |
|
|
| |
| binaryDirectory = argv0; |
| size_t pos = binaryDirectory.find_last_of("\\/"); |
| if (pos == std::string::npos) |
| binaryDirectory = "." + pathSeparator; |
| else |
| binaryDirectory.resize(pos + 1); |
|
|
| |
| if (binaryDirectory.find("." + pathSeparator) == 0) |
| binaryDirectory.replace(0, 1, workingDirectory); |
| } |
|
|
|
|
| } |
|
|
| } |
|
|