Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
official-stockfish
GitHub Repository: official-stockfish/Stockfish
Path: blob/master/src/misc.cpp
376 views
1
/*
2
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
4
5
Stockfish is free software: you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation, either version 3 of the License, or
8
(at your option) any later version.
9
10
Stockfish is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
GNU General Public License for more details.
14
15
You should have received a copy of the GNU General Public License
16
along with this program. If not, see <http://www.gnu.org/licenses/>.
17
*/
18
19
#include "misc.h"
20
21
#include <array>
22
#include <atomic>
23
#include <cassert>
24
#include <cctype>
25
#include <cmath>
26
#include <cstdlib>
27
#include <fstream>
28
#include <iomanip>
29
#include <iostream>
30
#include <iterator>
31
#include <limits>
32
#include <mutex>
33
#include <sstream>
34
#include <string_view>
35
36
#include "types.h"
37
38
namespace Stockfish {
39
40
namespace {
41
42
// Version number or dev.
43
constexpr std::string_view version = "dev";
44
45
// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
46
// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
47
// can toggle the logging of std::cout and std:cin at runtime whilst preserving
48
// usual I/O functionality, all without changing a single line of code!
49
// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81
50
51
struct Tie: public std::streambuf { // MSVC requires split streambuf for cin and cout
52
53
Tie(std::streambuf* b, std::streambuf* l) :
54
buf(b),
55
logBuf(l) {}
56
57
int sync() override { return logBuf->pubsync(), buf->pubsync(); }
58
int overflow(int c) override { return log(buf->sputc(char(c)), "<< "); }
59
int underflow() override { return buf->sgetc(); }
60
int uflow() override { return log(buf->sbumpc(), ">> "); }
61
62
std::streambuf *buf, *logBuf;
63
64
int log(int c, const char* prefix) {
65
66
static int last = '\n'; // Single log file
67
68
if (last == '\n')
69
logBuf->sputn(prefix, 3);
70
71
return last = logBuf->sputc(char(c));
72
}
73
};
74
75
class Logger {
76
77
Logger() :
78
in(std::cin.rdbuf(), file.rdbuf()),
79
out(std::cout.rdbuf(), file.rdbuf()) {}
80
~Logger() { start(""); }
81
82
std::ofstream file;
83
Tie in, out;
84
85
public:
86
static void start(const std::string& fname) {
87
88
static Logger l;
89
90
if (l.file.is_open())
91
{
92
std::cout.rdbuf(l.out.buf);
93
std::cin.rdbuf(l.in.buf);
94
l.file.close();
95
}
96
97
if (!fname.empty())
98
{
99
l.file.open(fname, std::ifstream::out);
100
101
if (!l.file.is_open())
102
{
103
std::cerr << "Unable to open debug log file " << fname << std::endl;
104
exit(EXIT_FAILURE);
105
}
106
107
std::cin.rdbuf(&l.in);
108
std::cout.rdbuf(&l.out);
109
}
110
}
111
};
112
113
} // namespace
114
115
116
// Returns the full name of the current Stockfish version.
117
//
118
// For local dev compiles we try to append the commit SHA and
119
// commit date from git. If that fails only the local compilation
120
// date is set and "nogit" is specified:
121
// Stockfish dev-YYYYMMDD-SHA
122
// or
123
// Stockfish dev-YYYYMMDD-nogit
124
//
125
// For releases (non-dev builds) we only include the version number:
126
// Stockfish version
127
std::string engine_version_info() {
128
std::stringstream ss;
129
ss << "Stockfish " << version << std::setfill('0');
130
131
if constexpr (version == "dev")
132
{
133
ss << "-";
134
#ifdef GIT_DATE
135
ss << stringify(GIT_DATE);
136
#else
137
constexpr std::string_view months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
138
139
std::string month, day, year;
140
std::stringstream date(__DATE__); // From compiler, format is "Sep 21 2008"
141
142
date >> month >> day >> year;
143
ss << year << std::setw(2) << std::setfill('0') << (1 + months.find(month) / 4)
144
<< std::setw(2) << std::setfill('0') << day;
145
#endif
146
147
ss << "-";
148
149
#ifdef GIT_SHA
150
ss << stringify(GIT_SHA);
151
#else
152
ss << "nogit";
153
#endif
154
}
155
156
return ss.str();
157
}
158
159
std::string engine_info(bool to_uci) {
160
return engine_version_info() + (to_uci ? "\nid author " : " by ")
161
+ "the Stockfish developers (see AUTHORS file)";
162
}
163
164
165
// Returns a string trying to describe the compiler we use
166
std::string compiler_info() {
167
168
#define make_version_string(major, minor, patch) \
169
stringify(major) "." stringify(minor) "." stringify(patch)
170
171
// Predefined macros hell:
172
//
173
// __GNUC__ Compiler is GCC, Clang or ICX
174
// __clang__ Compiler is Clang or ICX
175
// __INTEL_LLVM_COMPILER Compiler is ICX
176
// _MSC_VER Compiler is MSVC
177
// _WIN32 Building on Windows (any)
178
// _WIN64 Building on Windows 64 bit
179
180
std::string compiler = "\nCompiled by : ";
181
182
#if defined(__INTEL_LLVM_COMPILER)
183
compiler += "ICX ";
184
compiler += stringify(__INTEL_LLVM_COMPILER);
185
#elif defined(__clang__)
186
compiler += "clang++ ";
187
compiler += make_version_string(__clang_major__, __clang_minor__, __clang_patchlevel__);
188
#elif _MSC_VER
189
compiler += "MSVC ";
190
compiler += "(version ";
191
compiler += stringify(_MSC_FULL_VER) "." stringify(_MSC_BUILD);
192
compiler += ")";
193
#elif defined(__e2k__) && defined(__LCC__)
194
#define dot_ver2(n) \
195
compiler += char('.'); \
196
compiler += char('0' + (n) / 10); \
197
compiler += char('0' + (n) % 10);
198
199
compiler += "MCST LCC ";
200
compiler += "(version ";
201
compiler += std::to_string(__LCC__ / 100);
202
dot_ver2(__LCC__ % 100) dot_ver2(__LCC_MINOR__) compiler += ")";
203
#elif __GNUC__
204
compiler += "g++ (GNUC) ";
205
compiler += make_version_string(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
206
#else
207
compiler += "Unknown compiler ";
208
compiler += "(unknown version)";
209
#endif
210
211
#if defined(__APPLE__)
212
compiler += " on Apple";
213
#elif defined(__CYGWIN__)
214
compiler += " on Cygwin";
215
#elif defined(__MINGW64__)
216
compiler += " on MinGW64";
217
#elif defined(__MINGW32__)
218
compiler += " on MinGW32";
219
#elif defined(__ANDROID__)
220
compiler += " on Android";
221
#elif defined(__linux__)
222
compiler += " on Linux";
223
#elif defined(_WIN64)
224
compiler += " on Microsoft Windows 64-bit";
225
#elif defined(_WIN32)
226
compiler += " on Microsoft Windows 32-bit";
227
#else
228
compiler += " on unknown system";
229
#endif
230
231
compiler += "\nCompilation architecture : ";
232
#if defined(ARCH)
233
compiler += stringify(ARCH);
234
#else
235
compiler += "(undefined architecture)";
236
#endif
237
238
compiler += "\nCompilation settings : ";
239
compiler += (Is64Bit ? "64bit" : "32bit");
240
#if defined(USE_AVX512ICL)
241
compiler += " AVX512ICL";
242
#endif
243
#if defined(USE_VNNI)
244
compiler += " VNNI";
245
#endif
246
#if defined(USE_AVX512)
247
compiler += " AVX512";
248
#endif
249
compiler += (HasPext ? " BMI2" : "");
250
#if defined(USE_AVX2)
251
compiler += " AVX2";
252
#endif
253
#if defined(USE_SSE41)
254
compiler += " SSE41";
255
#endif
256
#if defined(USE_SSSE3)
257
compiler += " SSSE3";
258
#endif
259
#if defined(USE_SSE2)
260
compiler += " SSE2";
261
#endif
262
compiler += (HasPopCnt ? " POPCNT" : "");
263
#if defined(USE_NEON_DOTPROD)
264
compiler += " NEON_DOTPROD";
265
#elif defined(USE_NEON)
266
compiler += " NEON";
267
#endif
268
269
#if !defined(NDEBUG)
270
compiler += " DEBUG";
271
#endif
272
273
compiler += "\nCompiler __VERSION__ macro : ";
274
#ifdef __VERSION__
275
compiler += __VERSION__;
276
#else
277
compiler += "(undefined macro)";
278
#endif
279
280
compiler += "\n";
281
282
return compiler;
283
}
284
285
286
// Debug functions used mainly to collect run-time statistics
287
constexpr int MaxDebugSlots = 32;
288
289
namespace {
290
291
template<size_t N>
292
struct DebugInfo {
293
std::array<std::atomic<int64_t>, N> data = {0};
294
295
[[nodiscard]] constexpr std::atomic<int64_t>& operator[](size_t index) {
296
assert(index < N);
297
return data[index];
298
}
299
300
constexpr DebugInfo& operator=(const DebugInfo& other) {
301
for (size_t i = 0; i < N; i++)
302
data[i].store(other.data[i].load());
303
return *this;
304
}
305
};
306
307
struct DebugExtremes: public DebugInfo<3> {
308
DebugExtremes() {
309
data[1] = std::numeric_limits<int64_t>::min();
310
data[2] = std::numeric_limits<int64_t>::max();
311
}
312
};
313
314
std::array<DebugInfo<2>, MaxDebugSlots> hit;
315
std::array<DebugInfo<2>, MaxDebugSlots> mean;
316
std::array<DebugInfo<3>, MaxDebugSlots> stdev;
317
std::array<DebugInfo<6>, MaxDebugSlots> correl;
318
std::array<DebugExtremes, MaxDebugSlots> extremes;
319
320
} // namespace
321
322
void dbg_hit_on(bool cond, int slot) {
323
324
++hit.at(slot)[0];
325
if (cond)
326
++hit.at(slot)[1];
327
}
328
329
void dbg_mean_of(int64_t value, int slot) {
330
331
++mean.at(slot)[0];
332
mean.at(slot)[1] += value;
333
}
334
335
void dbg_stdev_of(int64_t value, int slot) {
336
337
++stdev.at(slot)[0];
338
stdev.at(slot)[1] += value;
339
stdev.at(slot)[2] += value * value;
340
}
341
342
void dbg_extremes_of(int64_t value, int slot) {
343
++extremes.at(slot)[0];
344
345
int64_t current_max = extremes.at(slot)[1].load();
346
while (current_max < value && !extremes.at(slot)[1].compare_exchange_weak(current_max, value))
347
{}
348
349
int64_t current_min = extremes.at(slot)[2].load();
350
while (current_min > value && !extremes.at(slot)[2].compare_exchange_weak(current_min, value))
351
{}
352
}
353
354
void dbg_correl_of(int64_t value1, int64_t value2, int slot) {
355
356
++correl.at(slot)[0];
357
correl.at(slot)[1] += value1;
358
correl.at(slot)[2] += value1 * value1;
359
correl.at(slot)[3] += value2;
360
correl.at(slot)[4] += value2 * value2;
361
correl.at(slot)[5] += value1 * value2;
362
}
363
364
void dbg_print() {
365
366
int64_t n;
367
auto E = [&n](int64_t x) { return double(x) / n; };
368
auto sqr = [](double x) { return x * x; };
369
370
for (int i = 0; i < MaxDebugSlots; ++i)
371
if ((n = hit[i][0]))
372
std::cerr << "Hit #" << i << ": Total " << n << " Hits " << hit[i][1]
373
<< " Hit Rate (%) " << 100.0 * E(hit[i][1]) << std::endl;
374
375
for (int i = 0; i < MaxDebugSlots; ++i)
376
if ((n = mean[i][0]))
377
{
378
std::cerr << "Mean #" << i << ": Total " << n << " Mean " << E(mean[i][1]) << std::endl;
379
}
380
381
for (int i = 0; i < MaxDebugSlots; ++i)
382
if ((n = stdev[i][0]))
383
{
384
double r = sqrt(E(stdev[i][2]) - sqr(E(stdev[i][1])));
385
std::cerr << "Stdev #" << i << ": Total " << n << " Stdev " << r << std::endl;
386
}
387
388
for (int i = 0; i < MaxDebugSlots; ++i)
389
if ((n = extremes[i][0]))
390
{
391
std::cerr << "Extremity #" << i << ": Total " << n << " Min " << extremes[i][2]
392
<< " Max " << extremes[i][1] << std::endl;
393
}
394
395
for (int i = 0; i < MaxDebugSlots; ++i)
396
if ((n = correl[i][0]))
397
{
398
double r = (E(correl[i][5]) - E(correl[i][1]) * E(correl[i][3]))
399
/ (sqrt(E(correl[i][2]) - sqr(E(correl[i][1])))
400
* sqrt(E(correl[i][4]) - sqr(E(correl[i][3]))));
401
std::cerr << "Correl. #" << i << ": Total " << n << " Coefficient " << r << std::endl;
402
}
403
}
404
405
void dbg_clear() {
406
hit.fill({});
407
mean.fill({});
408
stdev.fill({});
409
correl.fill({});
410
extremes.fill({});
411
}
412
413
// Used to serialize access to std::cout
414
// to avoid multiple threads writing at the same time.
415
std::ostream& operator<<(std::ostream& os, SyncCout sc) {
416
417
static std::mutex m;
418
419
if (sc == IO_LOCK)
420
m.lock();
421
422
if (sc == IO_UNLOCK)
423
m.unlock();
424
425
return os;
426
}
427
428
void sync_cout_start() { std::cout << IO_LOCK; }
429
void sync_cout_end() { std::cout << IO_UNLOCK; }
430
431
// Trampoline helper to avoid moving Logger to misc.h
432
void start_logger(const std::string& fname) { Logger::start(fname); }
433
434
435
#ifdef NO_PREFETCH
436
437
void prefetch(const void*) {}
438
439
#else
440
441
void prefetch(const void* addr) {
442
443
#if defined(_MSC_VER)
444
_mm_prefetch((char const*) addr, _MM_HINT_T0);
445
#else
446
__builtin_prefetch(addr);
447
#endif
448
}
449
450
#endif
451
452
#ifdef _WIN32
453
#include <direct.h>
454
#define GETCWD _getcwd
455
#else
456
#include <unistd.h>
457
#define GETCWD getcwd
458
#endif
459
460
size_t str_to_size_t(const std::string& s) {
461
unsigned long long value = std::stoull(s);
462
if (value > std::numeric_limits<size_t>::max())
463
std::exit(EXIT_FAILURE);
464
return static_cast<size_t>(value);
465
}
466
467
std::optional<std::string> read_file_to_string(const std::string& path) {
468
std::ifstream f(path, std::ios_base::binary);
469
if (!f)
470
return std::nullopt;
471
return std::string(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>());
472
}
473
474
void remove_whitespace(std::string& s) {
475
s.erase(std::remove_if(s.begin(), s.end(), [](char c) { return std::isspace(c); }), s.end());
476
}
477
478
bool is_whitespace(std::string_view s) {
479
return std::all_of(s.begin(), s.end(), [](char c) { return std::isspace(c); });
480
}
481
482
std::string CommandLine::get_binary_directory(std::string argv0) {
483
std::string pathSeparator;
484
485
#ifdef _WIN32
486
pathSeparator = "\\";
487
#ifdef _MSC_VER
488
// Under windows argv[0] may not have the extension. Also _get_pgmptr() had
489
// issues in some Windows 10 versions, so check returned values carefully.
490
char* pgmptr = nullptr;
491
if (!_get_pgmptr(&pgmptr) && pgmptr != nullptr && *pgmptr)
492
argv0 = pgmptr;
493
#endif
494
#else
495
pathSeparator = "/";
496
#endif
497
498
// Extract the working directory
499
auto workingDirectory = CommandLine::get_working_directory();
500
501
// Extract the binary directory path from argv0
502
auto binaryDirectory = argv0;
503
size_t pos = binaryDirectory.find_last_of("\\/");
504
if (pos == std::string::npos)
505
binaryDirectory = "." + pathSeparator;
506
else
507
binaryDirectory.resize(pos + 1);
508
509
// Pattern replacement: "./" at the start of path is replaced by the working directory
510
if (binaryDirectory.find("." + pathSeparator) == 0)
511
binaryDirectory.replace(0, 1, workingDirectory);
512
513
return binaryDirectory;
514
}
515
516
std::string CommandLine::get_working_directory() {
517
std::string workingDirectory = "";
518
char buff[40000];
519
char* cwd = GETCWD(buff, 40000);
520
if (cwd)
521
workingDirectory = cwd;
522
523
return workingDirectory;
524
}
525
526
527
} // namespace Stockfish
528
529