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:
Disservin
2026-07-06 11:30:51 +02:00
committed by Joost VandeVondele
co-authored by anematode
parent d5bbc6b67d
commit 22548cb0c0
14 changed files with 240 additions and 254 deletions
+45 -49
View File
@@ -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;
return false;
}
actualFilename = evalFile.defaultName;
sync_cout << "Failed to export a net. No network file is currently loaded. "
"Please load a network file first."
<< sync_endl;
return false;
}
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;
}