mirror of
https://github.com/official-stockfish/Stockfish.git
synced 2026-07-22 12:47:08 +00:00
S人ving Privàte Ryän
Fixes problems with non-ASCII characters in paths. Fixes #3424. Changes - Bump the minimum supported Clang version to 11 to support std::filesystem - Use std::optional for empty values instead of string sentinels like "None" - Remove the FixedString class - Move the EvalFile network path from the network struct to the engine - Simplify parts of the command-line handling - Remove the old dual-net logic from export_net - And ofc we now support non-ascii paths for windows Behavior Change - Because the EvalFile is no longer part of the network, it won't be included in the hashing, thus the network sharing is no longer dependent on the EvalFile path - As a result, if the binary version and network are the same, changing only the EvalFile location will now reuse the same shared network This also fixes a master bug where: ``` export_net None setoption name EvalFile value None export_net None Failed to export a net ``` would fail on the second export. With this patch, the second export succeeds. Quick Fishtest Check that nets load like normal. https://tests.stockfishchess.org/tests/view/6a455593f97ff95f787954da <img width="833" height="1137" alt="image" src="https://github.com/user-attachments/assets/fffee63b-c63a-4d5e-b13d-87d58bc28c88" /> closes https://github.com/official-stockfish/Stockfish/pull/6937 No functional change Co-authored-by: anematode <timothy.herchen@gmail.com>
This commit is contained in:
committed by
Joost VandeVondele
co-authored by
anematode
parent
d5bbc6b67d
commit
22548cb0c0
@@ -22,7 +22,6 @@ jobs:
|
||||
- { name: gcc-15, comp: gcc, cxx: g++, image: "gcc:15" }
|
||||
|
||||
# Using silkeh/clang for older versions
|
||||
- { name: clang-10, comp: clang, cxx: clang++, image: "silkeh/clang:10", is_clang: true, ver: "10" }
|
||||
- { name: clang-11, comp: clang, cxx: clang++, image: "silkeh/clang:11", is_clang: true, ver: "11" }
|
||||
- { name: clang-12, comp: clang, cxx: clang++, image: "silkeh/clang:12", is_clang: true, ver: "12" }
|
||||
- { name: clang-13, comp: clang, cxx: clang++, image: "silkeh/clang:13", is_clang: true, ver: "13" }
|
||||
|
||||
+18
-14
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <filesystem>
|
||||
#include <deque>
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
@@ -33,7 +34,6 @@
|
||||
#include "misc.h"
|
||||
#include "nnue/network.h"
|
||||
#include "nnue/nnue_common.h"
|
||||
#include "nnue/nnue_misc.h"
|
||||
#include "numa.h"
|
||||
#include "perft.h"
|
||||
#include "position.h"
|
||||
@@ -57,18 +57,19 @@ int MaxThreads = std::max(1024, 4 * int(get_hardware_concurrency()));
|
||||
// PR#6526). The user can always explicitly override this behavior.
|
||||
constexpr NumaAutoPolicy DefaultNumaPolicy = BundledL3Policy{32};
|
||||
|
||||
Engine::Engine(std::optional<std::string> path) :
|
||||
binaryDirectory(path ? CommandLine::get_binary_directory(*path) : ""),
|
||||
Engine::Engine(std::optional<std::filesystem::path> path) :
|
||||
binaryDirectory(path ? CommandLine::get_binary_directory(*path) : std::filesystem::path{}),
|
||||
numaContext(NumaConfig::from_system(DefaultNumaPolicy)),
|
||||
states(new std::deque<StateInfo>(1)),
|
||||
threads(),
|
||||
network(numaContext, get_default_network()) {
|
||||
networkFile{std::nullopt, ""},
|
||||
network(numaContext) {
|
||||
|
||||
pos.set(StartFEN, false, &states->back());
|
||||
|
||||
options.add( //
|
||||
"Debug Log File", Option("", [](const Option& o) {
|
||||
start_logger(o);
|
||||
start_logger(path_from_utf8(std::string(o)));
|
||||
return std::nullopt;
|
||||
}));
|
||||
|
||||
@@ -133,10 +134,11 @@ Engine::Engine(std::optional<std::string> path) :
|
||||
|
||||
options.add( //
|
||||
"EvalFile", Option(EvalFileDefaultName, [this](const Option& o) {
|
||||
load_network(o);
|
||||
load_network(path_from_utf8(std::string(o)));
|
||||
return std::nullopt;
|
||||
}));
|
||||
|
||||
network = get_default_network();
|
||||
threads.clear();
|
||||
threads.ensure_network_replicated();
|
||||
resize_threads();
|
||||
@@ -256,7 +258,8 @@ void Engine::set_ponderhit(bool b) { threads.main_manager()->ponder = b; }
|
||||
// network related
|
||||
|
||||
void Engine::verify_network() const {
|
||||
network->verify(options["EvalFile"], onVerifyNetwork);
|
||||
const auto file = path_from_utf8(std::string(options["EvalFile"]));
|
||||
network->verify(onVerifyNetwork, networkFile, file);
|
||||
|
||||
auto statuses = network.get_status_and_errors();
|
||||
for (usize i = 0; i < statuses.size(); ++i)
|
||||
@@ -289,24 +292,25 @@ void Engine::verify_network() const {
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Eval::NNUE::Network> Engine::get_default_network() const {
|
||||
std::unique_ptr<Eval::NNUE::Network> Engine::get_default_network() {
|
||||
|
||||
auto network_ = std::make_unique<NN::Network>(NN::EvalFile{EvalFileDefaultName, "None", ""});
|
||||
auto network_ = std::make_unique<NN::Network>();
|
||||
|
||||
network_->load(binaryDirectory, "");
|
||||
network_->load(binaryDirectory, std::filesystem::path{}, networkFile);
|
||||
|
||||
return network_;
|
||||
}
|
||||
|
||||
void Engine::load_network(const std::string& file) {
|
||||
void Engine::load_network(const std::filesystem::path& file) {
|
||||
network.modify_and_replicate(
|
||||
[this, &file](NN::Network& network_) { network_.load(binaryDirectory, file); });
|
||||
[this, &file](NN::Network& network_) { network_.load(binaryDirectory, file, networkFile); });
|
||||
threads.clear();
|
||||
threads.ensure_network_replicated();
|
||||
}
|
||||
|
||||
void Engine::save_network(const std::pair<std::optional<std::string>, std::string> file) {
|
||||
network.modify_and_replicate([&file](NN::Network& network_) { network_.save(file.first); });
|
||||
void Engine::save_network(const std::optional<std::filesystem::path>& file) {
|
||||
network.modify_and_replicate(
|
||||
[&file, this](NN::Network& network_) { network_.save(networkFile, file); });
|
||||
}
|
||||
|
||||
// utility functions
|
||||
|
||||
+8
-5
@@ -20,6 +20,7 @@
|
||||
#define ENGINE_H_INCLUDED
|
||||
|
||||
#include <functional>
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
@@ -31,6 +32,7 @@
|
||||
#include "misc.h"
|
||||
#include "history.h"
|
||||
#include "nnue/network.h"
|
||||
#include "nnue/nnue_misc.h"
|
||||
#include "numa.h"
|
||||
#include "position.h"
|
||||
#include "search.h"
|
||||
@@ -47,7 +49,7 @@ class Engine {
|
||||
using InfoFull = Search::InfoFull;
|
||||
using InfoIter = Search::InfoIteration;
|
||||
|
||||
Engine(std::optional<std::string> path = std::nullopt);
|
||||
Engine(std::optional<std::filesystem::path> path = std::nullopt);
|
||||
|
||||
// Cannot be movable due to components holding backreferences to fields
|
||||
Engine(const Engine&) = delete;
|
||||
@@ -87,9 +89,9 @@ class Engine {
|
||||
// network related
|
||||
|
||||
void verify_network() const;
|
||||
std::unique_ptr<Eval::NNUE::Network> get_default_network() const;
|
||||
void load_network(const std::string& file);
|
||||
void save_network(std::pair<std::optional<std::string>, std::string> file);
|
||||
std::unique_ptr<Eval::NNUE::Network> get_default_network();
|
||||
void load_network(const std::filesystem::path& file);
|
||||
void save_network(const std::optional<std::filesystem::path>& file);
|
||||
|
||||
// utility functions
|
||||
|
||||
@@ -110,7 +112,7 @@ class Engine {
|
||||
std::string thread_binding_information_as_string() const;
|
||||
|
||||
private:
|
||||
const std::string binaryDirectory;
|
||||
const std::filesystem::path binaryDirectory;
|
||||
|
||||
NumaReplicationContext numaContext;
|
||||
|
||||
@@ -120,6 +122,7 @@ class Engine {
|
||||
OptionsMap options;
|
||||
ThreadPool threads;
|
||||
TranspositionTable tt;
|
||||
Eval::NNUE::EvalFile networkFile;
|
||||
LazyNumaReplicatedSystemWide<Eval::NNUE::Network> network;
|
||||
|
||||
Search::SearchManager::UpdateContext updateContext;
|
||||
|
||||
+3
-1
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "attacks.h"
|
||||
#include "bitboard.h"
|
||||
@@ -43,7 +44,8 @@ int main(int argc, char* argv[]) {
|
||||
Attacks::init();
|
||||
Position::init();
|
||||
|
||||
auto uci = std::make_unique<UCIEngine>(argc, argv);
|
||||
auto cli = CommandLine(argc, argv);
|
||||
auto uci = std::make_unique<UCIEngine>(std::move(cli));
|
||||
|
||||
Tune::init(uci->engine_options());
|
||||
|
||||
|
||||
+79
-40
@@ -22,8 +22,10 @@
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
@@ -33,10 +35,21 @@
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <direct.h>
|
||||
#include <windows.h>
|
||||
#include <shellapi.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace {
|
||||
|
||||
// Version number or dev.
|
||||
@@ -83,7 +96,7 @@ class Logger {
|
||||
Tie in, out;
|
||||
|
||||
public:
|
||||
static void start(const std::string& fname) {
|
||||
static void start(const fs::path& fname) {
|
||||
|
||||
static Logger l;
|
||||
|
||||
@@ -474,16 +487,61 @@ u64 hash_bytes(const char* data, usize size) {
|
||||
}
|
||||
|
||||
// Trampoline helper to avoid moving Logger to misc.h
|
||||
void start_logger(const std::string& fname) { Logger::start(fname); }
|
||||
|
||||
void start_logger(const fs::path& fname) { Logger::start(fname); }
|
||||
|
||||
std::string utf8_from_wstring(std::wstring_view s) {
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#define GETCWD _getcwd
|
||||
if (s.empty())
|
||||
return {};
|
||||
|
||||
int size =
|
||||
WideCharToMultiByte(CP_UTF8, 0, s.data(), int(s.size()), nullptr, 0, nullptr, nullptr);
|
||||
if (size <= 0)
|
||||
return {};
|
||||
|
||||
std::string out(size, '\0');
|
||||
WideCharToMultiByte(CP_UTF8, 0, s.data(), int(s.size()), out.data(), size, nullptr, nullptr);
|
||||
return out;
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#define GETCWD getcwd
|
||||
return std::string(s.begin(), s.end());
|
||||
#endif
|
||||
}
|
||||
|
||||
fs::path path_from_utf8(const std::string& path) {
|
||||
#ifdef _WIN32
|
||||
int u8len = static_cast<int>(path.size());
|
||||
int wlen = MultiByteToWideChar(CP_UTF8, 0, path.c_str(), u8len, NULL, 0);
|
||||
|
||||
std::wstring wstr(static_cast<usize>(wlen), L'\0');
|
||||
MultiByteToWideChar(CP_UTF8, 0, path.c_str(), u8len, wstr.data(), wlen);
|
||||
return {wstr};
|
||||
#else
|
||||
return {path};
|
||||
#endif
|
||||
}
|
||||
|
||||
CommandLine::CommandLine(int _argc, char** _argv) :
|
||||
argc(_argc),
|
||||
argv(_argv) {
|
||||
#ifdef _WIN32
|
||||
// Convert any non-ANSI characters passed on the command line to UTF-8
|
||||
int wargc = 0;
|
||||
if (LPWSTR* wargv = CommandLineToArgvW(GetCommandLineW(), &wargc))
|
||||
{
|
||||
for (int i = 0; i < wargc; ++i)
|
||||
argv_storage.push_back(utf8_from_wstring(wargv[i]));
|
||||
LocalFree(wargv);
|
||||
|
||||
for (std::string& s : argv_storage)
|
||||
argv_utf8.push_back(s.data());
|
||||
argv_utf8.push_back(nullptr);
|
||||
|
||||
argc = wargc;
|
||||
argv = argv_utf8.data();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
usize str_to_size_t(const std::string& s) {
|
||||
unsigned long long value = std::stoull(s);
|
||||
@@ -507,49 +565,30 @@ bool is_whitespace(std::string_view s) {
|
||||
return std::all_of(s.begin(), s.end(), [](char c) { return std::isspace(c); });
|
||||
}
|
||||
|
||||
std::string CommandLine::get_binary_directory(std::string argv0) {
|
||||
std::string pathSeparator;
|
||||
fs::path CommandLine::get_binary_directory(fs::path argv0) {
|
||||
|
||||
#ifdef _WIN32
|
||||
pathSeparator = "\\";
|
||||
#ifdef _MSC_VER
|
||||
// Under windows argv[0] may not have the extension. Also _get_pgmptr() had
|
||||
// issues in some Windows 10 versions, so check returned values carefully.
|
||||
char* pgmptr = nullptr;
|
||||
if (!_get_pgmptr(&pgmptr) && pgmptr != nullptr && *pgmptr)
|
||||
argv0 = pgmptr;
|
||||
// Prefer the executable path reported by the CRT when available.
|
||||
wchar_t* pgmptr = nullptr;
|
||||
if (!_get_wpgmptr(&pgmptr) && pgmptr != nullptr && *pgmptr)
|
||||
argv0 = fs::path(pgmptr);
|
||||
#endif
|
||||
#else
|
||||
pathSeparator = "/";
|
||||
#endif
|
||||
|
||||
// Extract the working directory
|
||||
auto workingDirectory = CommandLine::get_working_directory();
|
||||
|
||||
// Extract the binary directory path from argv0
|
||||
auto binaryDirectory = argv0;
|
||||
usize pos = binaryDirectory.find_last_of("\\/");
|
||||
if (pos == std::string::npos)
|
||||
binaryDirectory = "." + pathSeparator;
|
||||
else
|
||||
binaryDirectory.resize(pos + 1);
|
||||
|
||||
// Pattern replacement: "./" at the start of path is replaced by the working directory
|
||||
if (binaryDirectory.find("." + pathSeparator) == 0)
|
||||
binaryDirectory.replace(0, 1, workingDirectory);
|
||||
|
||||
auto binaryDirectory = argv0.parent_path();
|
||||
if (binaryDirectory.empty())
|
||||
binaryDirectory = fs::path(".");
|
||||
return binaryDirectory;
|
||||
}
|
||||
|
||||
std::string CommandLine::get_working_directory() {
|
||||
std::string workingDirectory = "";
|
||||
char buff[40000];
|
||||
char* cwd = GETCWD(buff, 40000);
|
||||
if (cwd)
|
||||
workingDirectory = cwd;
|
||||
fs::path CommandLine::get_working_directory() { return std::filesystem::current_path(); }
|
||||
|
||||
return workingDirectory;
|
||||
void set_console_utf8() {
|
||||
#ifdef _WIN32
|
||||
SetConsoleCP(CP_UTF8);
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
+21
-86
@@ -30,10 +30,9 @@
|
||||
#include <exception> // IWYU pragma: keep
|
||||
// IWYU pragma: no_include <__exception/terminate.h>
|
||||
#include <functional>
|
||||
#include <filesystem>
|
||||
#include <iosfwd>
|
||||
#include <optional>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
@@ -132,10 +131,13 @@ void prefetch(const void* addr) {
|
||||
}
|
||||
#endif
|
||||
|
||||
void start_logger(const std::string& fname);
|
||||
void start_logger(const std::filesystem::path& fname);
|
||||
|
||||
usize str_to_size_t(const std::string& s);
|
||||
|
||||
std::string utf8_from_wstring(std::wstring_view s);
|
||||
std::filesystem::path path_from_utf8(const std::string& path);
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
struct PipeDeleter {
|
||||
@@ -524,88 +526,26 @@ inline void hash_combine(usize& seed, const T& v) {
|
||||
|
||||
inline u64 hash_string(const std::string& sv) { return hash_bytes(sv.data(), sv.size()); }
|
||||
|
||||
template<usize Capacity>
|
||||
class FixedString {
|
||||
public:
|
||||
FixedString() :
|
||||
length_(0) {
|
||||
data_[0] = '\0';
|
||||
}
|
||||
|
||||
FixedString(const char* str) {
|
||||
usize len = std::strlen(str);
|
||||
if (len > Capacity)
|
||||
std::terminate();
|
||||
std::memcpy(data_, str, len);
|
||||
length_ = len;
|
||||
data_[length_] = '\0';
|
||||
}
|
||||
|
||||
FixedString(const std::string& str) {
|
||||
if (str.size() > Capacity)
|
||||
std::terminate();
|
||||
std::memcpy(data_, str.data(), str.size());
|
||||
length_ = str.size();
|
||||
data_[length_] = '\0';
|
||||
}
|
||||
|
||||
usize size() const { return length_; }
|
||||
usize capacity() const { return Capacity; }
|
||||
|
||||
const char* c_str() const { return data_; }
|
||||
const char* data() const { return data_; }
|
||||
|
||||
char& operator[](usize i) { return data_[i]; }
|
||||
|
||||
const char& operator[](usize i) const { return data_[i]; }
|
||||
|
||||
FixedString& operator+=(const char* str) {
|
||||
usize len = std::strlen(str);
|
||||
if (length_ + len > Capacity)
|
||||
std::terminate();
|
||||
std::memcpy(data_ + length_, str, len);
|
||||
length_ += len;
|
||||
data_[length_] = '\0';
|
||||
return *this;
|
||||
}
|
||||
|
||||
FixedString& operator+=(const FixedString& other) { return (*this += other.c_str()); }
|
||||
|
||||
operator std::string() const { return std::string(data_, length_); }
|
||||
|
||||
operator std::string_view() const { return std::string_view(data_, length_); }
|
||||
|
||||
template<typename T>
|
||||
bool operator==(const T& other) const noexcept {
|
||||
return (std::string_view) (*this) == other;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool operator!=(const T& other) const noexcept {
|
||||
return (std::string_view) (*this) != other;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
length_ = 0;
|
||||
data_[0] = '\0';
|
||||
}
|
||||
|
||||
private:
|
||||
char data_[Capacity + 1]; // +1 for null terminator
|
||||
usize length_;
|
||||
};
|
||||
|
||||
struct CommandLine {
|
||||
public:
|
||||
CommandLine(int _argc, char** _argv) :
|
||||
argc(_argc),
|
||||
argv(_argv) {}
|
||||
CommandLine(int _argc, char** _argv);
|
||||
|
||||
static std::string get_binary_directory(std::string argv0);
|
||||
static std::string get_working_directory();
|
||||
CommandLine(const CommandLine&) = delete;
|
||||
CommandLine& operator=(const CommandLine&) = delete;
|
||||
CommandLine(CommandLine&&) = default;
|
||||
CommandLine& operator=(CommandLine&&) = default;
|
||||
|
||||
static std::filesystem::path get_binary_directory(std::filesystem::path argv0);
|
||||
static std::filesystem::path get_working_directory();
|
||||
|
||||
int argc;
|
||||
char** argv;
|
||||
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
std::vector<std::string> argv_storage;
|
||||
std::vector<char*> argv_utf8;
|
||||
#endif
|
||||
};
|
||||
|
||||
namespace Utility {
|
||||
@@ -666,13 +606,8 @@ void move_to_front(std::vector<T>& vec, Predicate pred) {
|
||||
#define RESTRICT
|
||||
#endif
|
||||
|
||||
void set_console_utf8();
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
template<Stockfish::usize N>
|
||||
struct std::hash<Stockfish::FixedString<N>> {
|
||||
Stockfish::usize operator()(const Stockfish::FixedString<N>& fstr) const noexcept {
|
||||
return Stockfish::hash_bytes(fstr.data(), fstr.size());
|
||||
}
|
||||
};
|
||||
|
||||
#endif // #ifndef MISC_H_INCLUDED
|
||||
|
||||
+42
-46
@@ -24,6 +24,7 @@
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
|
||||
#define INCBIN_SILENCE_BITCODE_WARNING
|
||||
#include "../incbin/incbin.h"
|
||||
@@ -58,8 +59,10 @@ const unsigned char gEmbeddedNNUEData[1] = {0x0};
|
||||
const unsigned int gEmbeddedNNUESize = 1;
|
||||
#endif
|
||||
|
||||
|
||||
namespace Stockfish::Eval::NNUE {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace Detail {
|
||||
|
||||
@@ -84,60 +87,56 @@ bool write_parameters(std::ostream& stream, const T& reference) {
|
||||
|
||||
} // namespace Detail
|
||||
|
||||
void Network::load(const std::string& rootDirectory, std::string evalfilePath) {
|
||||
void Network::load(const fs::path& rootDirectory, fs::path evalfilePath, EvalFile& evalFile) {
|
||||
#if defined(DEFAULT_NNUE_DIRECTORY)
|
||||
std::vector<std::string> dirs = {"<internal>", "", rootDirectory,
|
||||
stringify(DEFAULT_NNUE_DIRECTORY)};
|
||||
std::vector<fs::path> dirs = {fs::path{}, rootDirectory,
|
||||
fs::path(stringify(DEFAULT_NNUE_DIRECTORY))};
|
||||
#else
|
||||
std::vector<std::string> dirs = {"<internal>", "", rootDirectory};
|
||||
std::vector<fs::path> dirs = {fs::path{}, rootDirectory};
|
||||
#endif
|
||||
|
||||
if (evalfilePath.empty())
|
||||
evalfilePath = evalFile.defaultName;
|
||||
|
||||
if (evalFile.current != evalfilePath && evalfilePath == evalFile.defaultName)
|
||||
load_internal(evalFile);
|
||||
|
||||
for (const auto& directory : dirs)
|
||||
{
|
||||
if (std::string(evalFile.current) != evalfilePath)
|
||||
{
|
||||
if (directory != "<internal>")
|
||||
load_user_net(directory, evalfilePath);
|
||||
else if (evalfilePath == std::string(evalFile.defaultName))
|
||||
load_internal();
|
||||
}
|
||||
if (evalFile.current != evalfilePath)
|
||||
load_external(directory, evalfilePath, evalFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Network::save(const std::optional<std::string>& filename) const {
|
||||
std::string actualFilename;
|
||||
std::string msg;
|
||||
|
||||
if (filename.has_value())
|
||||
actualFilename = filename.value();
|
||||
else
|
||||
bool Network::save(const EvalFile& evalFile, const std::optional<fs::path>& filename) const {
|
||||
if (!evalFile.current.has_value())
|
||||
{
|
||||
if (std::string(evalFile.current) != std::string(evalFile.defaultName))
|
||||
{
|
||||
msg = "Failed to export a net. "
|
||||
"A non-embedded net can only be saved if the filename is specified";
|
||||
|
||||
sync_cout << msg << sync_endl;
|
||||
sync_cout << "Failed to export a net. No network file is currently loaded. "
|
||||
"Please load a network file first."
|
||||
<< sync_endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
actualFilename = evalFile.defaultName;
|
||||
if (!filename.has_value() && evalFile.current != evalFile.defaultName)
|
||||
{
|
||||
sync_cout << "Failed to export a net. A non-embedded net can only be "
|
||||
"saved if the filename is specified"
|
||||
<< sync_endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
fs::path actualFilename = filename.value_or(evalFile.defaultName);
|
||||
std::ofstream stream(actualFilename, std::ios_base::binary);
|
||||
bool saved = save(stream, evalFile.current, evalFile.netDescription);
|
||||
|
||||
msg = saved ? "Network saved successfully to " + actualFilename : "Failed to export a net";
|
||||
bool saved = save(stream, evalFile.netDescription);
|
||||
|
||||
sync_cout << (saved ? "Network saved successfully to " + actualFilename.string()
|
||||
: "Failed to export a net")
|
||||
<< sync_endl;
|
||||
|
||||
sync_cout << msg << sync_endl;
|
||||
return saved;
|
||||
}
|
||||
|
||||
|
||||
NetworkOutput Network::evaluate(const Position& pos,
|
||||
AccumulatorStack& accumulatorStack,
|
||||
AccumulatorCaches& cache) const {
|
||||
@@ -158,18 +157,20 @@ NetworkOutput Network::evaluate(const Position& pos,
|
||||
}
|
||||
|
||||
|
||||
void Network::verify(std::string evalfilePath,
|
||||
const std::function<void(std::string_view)>& f) const {
|
||||
void Network::verify(const std::function<void(std::string_view)>& f,
|
||||
const EvalFile& evalFile,
|
||||
fs::path evalfilePath) const {
|
||||
if (evalfilePath.empty())
|
||||
evalfilePath = evalFile.defaultName;
|
||||
|
||||
if (std::string(evalFile.current) != evalfilePath)
|
||||
if (evalFile.current != evalfilePath)
|
||||
{
|
||||
if (f)
|
||||
{
|
||||
std::string msg1 =
|
||||
"Network evaluation parameters compatible with the engine must be available.";
|
||||
std::string msg2 = "The network file " + evalfilePath + " was not loaded successfully.";
|
||||
std::string msg2 =
|
||||
"The network file " + evalfilePath.string() + " was not loaded successfully.";
|
||||
std::string msg3 = "The UCI option EvalFile might need to specify the full path, "
|
||||
"including the directory name, to the network file.";
|
||||
std::string msg4 = "The default net can be downloaded from: "
|
||||
@@ -189,8 +190,9 @@ void Network::verify(std::string evalfilePath,
|
||||
if (f)
|
||||
{
|
||||
usize size = sizeof(featureTransformer) + sizeof(NetworkArchitecture) * LayerStacks;
|
||||
f("NNUE evaluation using " + evalfilePath + " (" + std::to_string(size / (1024 * 1024))
|
||||
+ "MiB, (" + std::to_string(featureTransformer.InputDimensions) + ", "
|
||||
f("NNUE evaluation using " + evalfilePath.string() + " ("
|
||||
+ std::to_string(size / (1024 * 1024)) + "MiB, ("
|
||||
+ std::to_string(featureTransformer.InputDimensions) + ", "
|
||||
+ std::to_string(network[0].TransformedFeatureDimensions) + ", "
|
||||
+ std::to_string(network[0].FC_0_OUTPUTS) + ", " + std::to_string(network[0].FC_1_OUTPUTS)
|
||||
+ ", 1))");
|
||||
@@ -225,8 +227,8 @@ NnueEvalTrace Network::trace_evaluate(const Position& pos,
|
||||
}
|
||||
|
||||
|
||||
void Network::load_user_net(const std::string& dir, const std::string& evalfilePath) {
|
||||
std::ifstream stream(dir + evalfilePath, std::ios::binary);
|
||||
void Network::load_external(const fs::path& dir, const fs::path& evalfilePath, EvalFile& evalFile) {
|
||||
std::ifstream stream(dir / evalfilePath, std::ios::binary);
|
||||
auto description = load(stream);
|
||||
|
||||
if (description.has_value())
|
||||
@@ -237,7 +239,7 @@ void Network::load_user_net(const std::string& dir, const std::string& evalfileP
|
||||
}
|
||||
|
||||
|
||||
void Network::load_internal() {
|
||||
void Network::load_internal(EvalFile& evalFile) {
|
||||
// C++ way to prepare a buffer for a memory stream
|
||||
class MemoryBuffer: public std::basic_streambuf<char> {
|
||||
public:
|
||||
@@ -269,12 +271,7 @@ void Network::load_internal() {
|
||||
void Network::initialize() { initialized = true; }
|
||||
|
||||
|
||||
bool Network::save(std::ostream& stream,
|
||||
const std::string& name,
|
||||
const std::string& netDescription) const {
|
||||
if (name.empty() || name == "None")
|
||||
return false;
|
||||
|
||||
bool Network::save(std::ostream& stream, const std::string& netDescription) const {
|
||||
return write_parameters(stream, netDescription);
|
||||
}
|
||||
|
||||
@@ -295,7 +292,6 @@ usize Network::get_content_hash() const {
|
||||
hash_combine(h, featureTransformer);
|
||||
for (auto&& layerstack : network)
|
||||
hash_combine(h, layerstack);
|
||||
hash_combine(h, evalFile);
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
+14
-11
@@ -26,6 +26,7 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <filesystem>
|
||||
|
||||
#include "../types.h"
|
||||
#include "../misc.h"
|
||||
@@ -49,8 +50,7 @@ using NetworkOutput = std::tuple<Value, Value>;
|
||||
// there is no way to run destructors.
|
||||
class Network {
|
||||
public:
|
||||
Network(EvalFile file) :
|
||||
evalFile(file) {}
|
||||
Network() = default;
|
||||
|
||||
Network(const Network& other) = default;
|
||||
Network(Network&& other) = default;
|
||||
@@ -58,8 +58,10 @@ class Network {
|
||||
Network& operator=(const Network& other) = default;
|
||||
Network& operator=(Network&& other) = default;
|
||||
|
||||
void load(const std::string& rootDirectory, std::string evalfilePath);
|
||||
bool save(const std::optional<std::string>& filename) const;
|
||||
void load(const std::filesystem::path& rootDirectory,
|
||||
std::filesystem::path evalfilePath,
|
||||
EvalFile& evalFile);
|
||||
bool save(const EvalFile& evalFile, const std::optional<std::filesystem::path>& filename) const;
|
||||
|
||||
usize get_content_hash() const;
|
||||
|
||||
@@ -68,18 +70,21 @@ class Network {
|
||||
AccumulatorCaches& cache) const;
|
||||
|
||||
|
||||
void verify(std::string evalfilePath, const std::function<void(std::string_view)>&) const;
|
||||
void verify(const std::function<void(std::string_view)>& f,
|
||||
const EvalFile& evalFile,
|
||||
std::filesystem::path evalfilePath) const;
|
||||
|
||||
NnueEvalTrace trace_evaluate(const Position& pos,
|
||||
AccumulatorStack& accumulatorStack,
|
||||
AccumulatorCaches& cache) const;
|
||||
|
||||
private:
|
||||
void load_user_net(const std::string&, const std::string&);
|
||||
void load_internal();
|
||||
void load_external(const std::filesystem::path&, const std::filesystem::path&, EvalFile&);
|
||||
void load_internal(EvalFile&);
|
||||
|
||||
private:
|
||||
void initialize();
|
||||
|
||||
bool save(std::ostream&, const std::string&, const std::string&) const;
|
||||
bool save(std::ostream&, const std::string&) const;
|
||||
std::optional<std::string> load(std::istream&);
|
||||
|
||||
bool read_header(std::istream&, u32*, std::string*) const;
|
||||
@@ -94,8 +99,6 @@ class Network {
|
||||
// Evaluation function
|
||||
NetworkArchitecture network[LayerStacks];
|
||||
|
||||
EvalFile evalFile;
|
||||
|
||||
bool initialized = false;
|
||||
|
||||
// Hash value of evaluation function structure
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <iomanip>
|
||||
#include <iosfwd>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include "../position.h"
|
||||
|
||||
+9
-16
@@ -19,12 +19,15 @@
|
||||
#ifndef NNUE_MISC_H_INCLUDED
|
||||
#define NNUE_MISC_H_INCLUDED
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <optional>
|
||||
#include <filesystem>
|
||||
|
||||
#include "../misc.h"
|
||||
#include "../types.h"
|
||||
#include "nnue_architecture.h"
|
||||
#include "../evaluate.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
@@ -32,15 +35,16 @@ class Position;
|
||||
|
||||
namespace Eval::NNUE {
|
||||
|
||||
// EvalFile uses fixed string types because it's part of the network structure which must be trivial.
|
||||
// NNUE file metadata uses fixed string types so it stays trivially copyable and cheap to move
|
||||
// around between the engine and the network loader.
|
||||
struct EvalFile {
|
||||
// Default net name, will use one of the EvalFileDefaultName* macros defined
|
||||
// in evaluate.h
|
||||
FixedString<256> defaultName;
|
||||
constexpr static std::string_view defaultName = EvalFileDefaultName;
|
||||
// Selected net name, either via uci option or default
|
||||
FixedString<256> current;
|
||||
std::optional<std::filesystem::path> current;
|
||||
// Net description extracted from the net file
|
||||
FixedString<256> netDescription;
|
||||
std::string netDescription;
|
||||
};
|
||||
|
||||
struct NnueEvalTrace {
|
||||
@@ -59,15 +63,4 @@ std::string trace(Position& pos, const Network& network, AccumulatorCaches& cach
|
||||
} // namespace Stockfish::Eval::NNUE
|
||||
} // namespace Stockfish
|
||||
|
||||
template<>
|
||||
struct std::hash<Stockfish::Eval::NNUE::EvalFile> {
|
||||
Stockfish::usize operator()(const Stockfish::Eval::NNUE::EvalFile& evalFile) const noexcept {
|
||||
Stockfish::usize h = 0;
|
||||
Stockfish::hash_combine(h, evalFile.defaultName);
|
||||
Stockfish::hash_combine(h, evalFile.current);
|
||||
Stockfish::hash_combine(h, evalFile.netDescription);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // #ifndef NNUE_MISC_H_INCLUDED
|
||||
|
||||
+29
-21
@@ -32,6 +32,7 @@
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
#include <filesystem>
|
||||
#include <sys/stat.h>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
@@ -227,7 +228,7 @@ static_assert(sizeof(LR) == 3, "LR tree entry must be 3 bytes");
|
||||
// time only existence of the file is checked.
|
||||
class TBFile: public std::ifstream {
|
||||
|
||||
std::string fname;
|
||||
std::filesystem::path fname;
|
||||
|
||||
public:
|
||||
// Look for and open the file among the Paths directories where the .rtbw
|
||||
@@ -236,21 +237,13 @@ class TBFile: public std::ifstream {
|
||||
//
|
||||
// Example:
|
||||
// C:\tb\wdl345;C:\tb\wdl6;D:\tb\dtz345;D:\tb\dtz6
|
||||
static std::string Paths;
|
||||
static std::vector<std::filesystem::path> Paths;
|
||||
|
||||
TBFile(const std::string& f) {
|
||||
|
||||
#ifndef _WIN32
|
||||
constexpr char SepChar = ':';
|
||||
#else
|
||||
constexpr char SepChar = ';';
|
||||
#endif
|
||||
std::stringstream ss(Paths);
|
||||
std::string path;
|
||||
|
||||
while (std::getline(ss, path, SepChar))
|
||||
for (const auto& path : Paths)
|
||||
{
|
||||
fname = path + "/" + f;
|
||||
fname = path / std::filesystem::path(f);
|
||||
std::ifstream::open(fname);
|
||||
if (is_open())
|
||||
return;
|
||||
@@ -273,7 +266,7 @@ class TBFile: public std::ifstream {
|
||||
|
||||
if (statbuf.st_size % 64 != 16)
|
||||
{
|
||||
std::cerr << "Corrupt tablebase file " << fname << std::endl;
|
||||
std::cerr << "Corrupt tablebase file " << fname.string() << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@@ -286,12 +279,12 @@ class TBFile: public std::ifstream {
|
||||
|
||||
if (*baseAddress == MAP_FAILED)
|
||||
{
|
||||
std::cerr << "Could not mmap() " << fname << std::endl;
|
||||
std::cerr << "Could not mmap() " << fname.string() << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#else
|
||||
// Note FILE_FLAG_RANDOM_ACCESS is only a hint to Windows and as such may get ignored.
|
||||
HANDLE fd = CreateFileA(fname.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
|
||||
HANDLE fd = CreateFileW(fname.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
|
||||
OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, nullptr);
|
||||
|
||||
if (fd == INVALID_HANDLE_VALUE)
|
||||
@@ -302,7 +295,7 @@ class TBFile: public std::ifstream {
|
||||
|
||||
if (size_low % 64 != 16)
|
||||
{
|
||||
std::cerr << "Corrupt tablebase file " << fname << std::endl;
|
||||
std::cerr << "Corrupt tablebase file " << fname.string() << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@@ -320,7 +313,7 @@ class TBFile: public std::ifstream {
|
||||
|
||||
if (!*baseAddress)
|
||||
{
|
||||
std::cerr << "MapViewOfFile() failed, name = " << fname
|
||||
std::cerr << "MapViewOfFile() failed, name = " << fname.string()
|
||||
<< ", error = " << GetLastError() << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -331,7 +324,7 @@ class TBFile: public std::ifstream {
|
||||
|
||||
if (memcmp(data, Magics[type == WDL], 4))
|
||||
{
|
||||
std::cerr << "Corrupted table in file " << fname << std::endl;
|
||||
std::cerr << "Corrupted table in file " << fname.string() << std::endl;
|
||||
unmap(*baseAddress, *mapping);
|
||||
return *baseAddress = nullptr, nullptr;
|
||||
}
|
||||
@@ -350,7 +343,7 @@ class TBFile: public std::ifstream {
|
||||
}
|
||||
};
|
||||
|
||||
std::string TBFile::Paths;
|
||||
std::vector<std::filesystem::path> TBFile::Paths;
|
||||
|
||||
// struct PairsData contains low-level indexing information to access TB data.
|
||||
// There are 8, 4, or 2 PairsData records for each TBTable, according to the type
|
||||
@@ -1405,9 +1398,24 @@ void Tablebases::init(const std::string& paths) {
|
||||
|
||||
TBTables.clear();
|
||||
MaxCardinality = 0;
|
||||
TBFile::Paths = paths;
|
||||
TBFile::Paths.clear();
|
||||
|
||||
if (paths.empty())
|
||||
if (!paths.empty())
|
||||
{
|
||||
#ifndef _WIN32
|
||||
constexpr char SepChar = ':';
|
||||
#else
|
||||
constexpr char SepChar = ';';
|
||||
#endif
|
||||
|
||||
std::stringstream ss(paths);
|
||||
std::string path;
|
||||
while (std::getline(ss, path, SepChar))
|
||||
if (!path.empty())
|
||||
TBFile::Paths.emplace_back(path_from_utf8(path));
|
||||
}
|
||||
|
||||
if (TBFile::Paths.empty())
|
||||
return;
|
||||
|
||||
// MapB1H1H7[] encodes a square below a1-h8 diagonal to 0..27
|
||||
|
||||
+2
-2
@@ -65,8 +65,8 @@
|
||||
#endif
|
||||
|
||||
// Enforce minimum Clang version
|
||||
#if defined(__clang__) && (__clang_major__ < 10)
|
||||
#error "Stockfish requires Clang 10.0 or later for correct compilation"
|
||||
#if defined(__clang__) && (__clang_major__ < 11)
|
||||
#error "Stockfish requires Clang 11.0 or later for correct compilation"
|
||||
#endif
|
||||
|
||||
#define ASSERT_ALIGNED(ptr, alignment) assert(reinterpret_cast<uintptr_t>(ptr) % alignment == 0)
|
||||
|
||||
+10
-7
@@ -26,6 +26,7 @@
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
#include <filesystem>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -63,9 +64,9 @@ void UCIEngine::print_info_string(std::string_view str) {
|
||||
sync_cout_end();
|
||||
}
|
||||
|
||||
UCIEngine::UCIEngine(int argc, char** argv) :
|
||||
engine(argv[0]),
|
||||
cli(argc, argv) {
|
||||
UCIEngine::UCIEngine(CommandLine cli_) :
|
||||
engine(cli_.argc > 0 ? std::optional{path_from_utf8(cli_.argv[0])} : std::nullopt),
|
||||
cli(std::move(cli_)) {
|
||||
|
||||
engine.get_options().add_info_listener([](const std::optional<std::string>& str) {
|
||||
if (str.has_value())
|
||||
@@ -85,6 +86,7 @@ void UCIEngine::init_search_update_listeners() {
|
||||
}
|
||||
|
||||
void UCIEngine::loop() {
|
||||
set_console_utf8();
|
||||
std::string token, cmd;
|
||||
|
||||
for (int i = 1; i < cli.argc; ++i)
|
||||
@@ -151,10 +153,11 @@ void UCIEngine::loop() {
|
||||
sync_cout << compiler_info() << sync_endl;
|
||||
else if (token == "export_net")
|
||||
{
|
||||
std::pair<std::optional<std::string>, std::string> file;
|
||||
std::optional<std::filesystem::path> file;
|
||||
std::string filename;
|
||||
|
||||
if (is >> file.second)
|
||||
file.first = file.second;
|
||||
if (is >> filename)
|
||||
file = path_from_utf8(filename);
|
||||
|
||||
engine.save_network(file);
|
||||
}
|
||||
@@ -171,7 +174,7 @@ void UCIEngine::loop() {
|
||||
sync_cout << "Unknown command: '" << cmd << "'. Type help for more information."
|
||||
<< sync_endl;
|
||||
|
||||
} while (token != "quit" && cli.argc == 1); // The command-line arguments are one-shot
|
||||
} while (token != "quit" && cli.argc <= 1); // The command-line arguments are one-shot
|
||||
}
|
||||
|
||||
Search::LimitsType UCIEngine::parse_limits(std::istream& is) {
|
||||
|
||||
Reference in New Issue
Block a user