]> git.sesse.net Git - stockfish/blob - src/misc.cpp
Handle Windows Processors Groups
[stockfish] / src / misc.cpp
1 /*
2   Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3   Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
4   Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
5   Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
6
7   Stockfish is free software: you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation, either version 3 of the License, or
10   (at your option) any later version.
11
12   Stockfish is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #ifdef _WIN32
22 #if _WIN32_WINNT < 0x0601
23 #undef  _WIN32_WINNT
24 #define _WIN32_WINNT 0x0601 // Force to include newest API (Win 7 or later)
25 #endif
26 #include <windows.h> // For processor groups
27 #endif
28
29 #include <fstream>
30 #include <iomanip>
31 #include <iostream>
32 #include <sstream>
33 #include <vector>
34
35 #include "misc.h"
36 #include "thread.h"
37
38 using namespace std;
39
40 namespace {
41
42 /// Version number. If Version is left empty, then compile date in the format
43 /// DD-MM-YY and show in engine_info.
44 const string Version = "";
45
46 /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
47 /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
48 /// can toggle the logging of std::cout and std:cin at runtime whilst preserving
49 /// usual I/O functionality, all without changing a single line of code!
50 /// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81
51
52 struct Tie: public streambuf { // MSVC requires split streambuf for cin and cout
53
54   Tie(streambuf* b, streambuf* l) : buf(b), logBuf(l) {}
55
56   int sync() { return logBuf->pubsync(), buf->pubsync(); }
57   int overflow(int c) { return log(buf->sputc((char)c), "<< "); }
58   int underflow() { return buf->sgetc(); }
59   int uflow() { return log(buf->sbumpc(), ">> "); }
60
61   streambuf *buf, *logBuf;
62
63   int log(int c, const char* prefix) {
64
65     static int last = '\n'; // Single log file
66
67     if (last == '\n')
68         logBuf->sputn(prefix, 3);
69
70     return last = logBuf->sputc((char)c);
71   }
72 };
73
74 class Logger {
75
76   Logger() : in(cin.rdbuf(), file.rdbuf()), out(cout.rdbuf(), file.rdbuf()) {}
77  ~Logger() { start(""); }
78
79   ofstream file;
80   Tie in, out;
81
82 public:
83   static void start(const std::string& fname) {
84
85     static Logger l;
86
87     if (!fname.empty() && !l.file.is_open())
88     {
89         l.file.open(fname, ifstream::out);
90         cin.rdbuf(&l.in);
91         cout.rdbuf(&l.out);
92     }
93     else if (fname.empty() && l.file.is_open())
94     {
95         cout.rdbuf(l.out.buf);
96         cin.rdbuf(l.in.buf);
97         l.file.close();
98     }
99   }
100 };
101
102 } // namespace
103
104 /// engine_info() returns the full name of the current Stockfish version. This
105 /// will be either "Stockfish <Tag> DD-MM-YY" (where DD-MM-YY is the date when
106 /// the program was compiled) or "Stockfish <Version>", depending on whether
107 /// Version is empty.
108
109 const string engine_info(bool to_uci) {
110
111   const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
112   string month, day, year;
113   stringstream ss, date(__DATE__); // From compiler, format is "Sep 21 2008"
114
115   ss << "Stockfish " << Version << setfill('0');
116
117   if (Version.empty())
118   {
119       date >> month >> day >> year;
120       ss << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2);
121   }
122
123   ss << (Is64Bit ? " 64" : "")
124      << (HasPext ? " BMI2" : (HasPopCnt ? " POPCNT" : ""))
125      << (to_uci  ? "\nid author ": " by ")
126      << "T. Romstad, M. Costalba, J. Kiiski, G. Linscott";
127
128   return ss.str();
129 }
130
131
132 /// Debug functions used mainly to collect run-time statistics
133 static int64_t hits[2], means[2];
134
135 void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; }
136 void dbg_hit_on(bool c, bool b) { if (c) dbg_hit_on(b); }
137 void dbg_mean_of(int v) { ++means[0]; means[1] += v; }
138
139 void dbg_print() {
140
141   if (hits[0])
142       cerr << "Total " << hits[0] << " Hits " << hits[1]
143            << " hit rate (%) " << 100 * hits[1] / hits[0] << endl;
144
145   if (means[0])
146       cerr << "Total " << means[0] << " Mean "
147            << (double)means[1] / means[0] << endl;
148 }
149
150
151 /// Used to serialize access to std::cout to avoid multiple threads writing at
152 /// the same time.
153
154 std::ostream& operator<<(std::ostream& os, SyncCout sc) {
155
156   static Mutex m;
157
158   if (sc == IO_LOCK)
159       m.lock();
160
161   if (sc == IO_UNLOCK)
162       m.unlock();
163
164   return os;
165 }
166
167
168 /// Trampoline helper to avoid moving Logger to misc.h
169 void start_logger(const std::string& fname) { Logger::start(fname); }
170
171
172 /// prefetch() preloads the given address in L1/L2 cache. This is a non-blocking
173 /// function that doesn't stall the CPU waiting for data to be loaded from memory,
174 /// which can be quite slow.
175 #ifdef NO_PREFETCH
176
177 void prefetch(void*) {}
178
179 #else
180
181 void prefetch(void* addr) {
182
183 #  if defined(__INTEL_COMPILER)
184    // This hack prevents prefetches from being optimized away by
185    // Intel compiler. Both MSVC and gcc seem not be affected by this.
186    __asm__ ("");
187 #  endif
188
189 #  if defined(__INTEL_COMPILER) || defined(_MSC_VER)
190   _mm_prefetch((char*)addr, _MM_HINT_T0);
191 #  else
192   __builtin_prefetch(addr);
193 #  endif
194 }
195
196 #endif
197
198 namespace WinProcGroup {
199
200 #ifndef _WIN32
201
202 void bindThisThread(size_t) {}
203
204 #else
205
206 /// get_group() retrieves logical processor information using Windows specific
207 /// API and returns the best group id for the thread with index idx. Original
208 /// code from Texel by Peter Ă–sterlund.
209
210 int get_group(size_t idx) {
211
212   int threads = 0;
213   int nodes = 0;
214   int cores = 0;
215   DWORD returnLength = 0;
216   DWORD byteOffset = 0;
217
218   // Early exit if the needed API are not available at runtime
219   HMODULE k32 = GetModuleHandle("Kernel32.dll");
220   if (   !GetProcAddress(k32, "GetLogicalProcessorInformationEx")
221       || !GetProcAddress(k32, "GetNumaNodeProcessorMaskEx")
222       || !GetProcAddress(k32, "SetThreadGroupAffinity"))
223       return -1;
224
225   // First call to get returnLength. We expect it to fail due to null buffer
226   if (GetLogicalProcessorInformationEx(RelationAll, nullptr, &returnLength))
227       return -1;
228
229   // Once we know returnLength, allocate the buffer
230   SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *buffer, *ptr;
231   ptr = buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(returnLength);
232
233   // Second call, now we expect to succeed
234   if (!GetLogicalProcessorInformationEx(RelationAll, buffer, &returnLength))
235   {
236       free(buffer);
237       return -1;
238   }
239
240   while (ptr->Size > 0 && byteOffset + ptr->Size <= returnLength)
241   {
242       if (ptr->Relationship == RelationNumaNode)
243           nodes++;
244
245       else if (ptr->Relationship == RelationProcessorCore)
246       {
247           cores++;
248           threads += (ptr->Processor.Flags == LTP_PC_SMT) ? 2 : 1;
249       }
250
251       byteOffset += ptr->Size;
252       ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)(((char*)ptr) + ptr->Size);
253   }
254
255   free(buffer);
256
257   std::vector<int> groups;
258
259   // Run as many threads as possible on the same node until core limit is
260   // reached, then move on filling the next node.
261   for (int n = 0; n < nodes; n++)
262       for (int i = 0; i < cores / nodes; i++)
263           groups.push_back(n);
264
265   // In case a core has more than one logical processor (we assume 2) and we
266   // have still threads to allocate, then spread them evenly across available
267   // nodes.
268   for (int t = 0; t < threads - cores; t++)
269       groups.push_back(t % nodes);
270
271   // If we still have more threads than the total number of logical processors
272   // then return -1 and let the OS to decide what to do.
273   return idx < groups.size() ? groups[idx] : -1;
274 }
275
276
277 /// bindThisThread() set the group affinity of the current thread
278
279 void bindThisThread(size_t idx) {
280
281   // Use a local variable instead of a static: slower but thread-safe
282   int group = get_group(idx);
283
284   if (group == -1)
285       return;
286
287   GROUP_AFFINITY mask;
288   if (GetNumaNodeProcessorMaskEx(group, &mask))
289       SetThreadGroupAffinity(GetCurrentThread(), &mask, nullptr);
290 }
291
292 #endif
293
294 } // namespace WinProcGroup