mirror of
https://github.com/official-stockfish/Stockfish.git
synced 2026-07-22 12:47:08 +00:00
Remove small net
Failed VVLTC non-regression https://tests.stockfishchess.org/tests/view/69d562b84088e069540a2288 LLR: -2.96 (-2.94,2.94) <-1.75,0.25> Total: 386998 W: 99181 L: 99760 D: 188057 Ptnml(0-2): 35, 35792, 122429, 35203, 40 Failed STC non-regression https://tests.stockfishchess.org/tests/view/69f3c6601e5788938e86a99e LLR: -2.93 (-2.94,2.94) <-1.75,0.25> Total: 33696 W: 8492 L: 8795 D: 16409 Ptnml(0-2): 124, 4209, 8504, 3868, 143 Many thanks to Dubslow, Torom, ces42, Shawn, vondele, Disservin and others for discussion. ## Summary The venerable small net has been around for quite some time now, and while the big net architecture has substantially advanced with TI, the small net has stayed with plain HalfKA. It therefore presents a few burdens: multiple net architectures to maintain, multiple nets to train, and a whole lot of templates to deal with the variable L1 size. Locally I measure a slowdown of -2.5% in NPS with this branch – and it's probably more on non-AVX512 architectures – but a pure slowdown of that magnitude would lead to more dramatic losses (even at VVLTC) than exhibited in the above tests, suggesting that the small net's lower eval quality is deleterious. Bonus: Shawn found this interesting PGN among the VVLTC games: https://lichess.org/study/hvo8jflc/OeTOityv `master` seems to misevaluate the fortress because all positions go to small net (the material difference is larger than the threshold). closes https://github.com/official-stockfish/Stockfish/pull/6796 Bench: 2877007
This commit is contained in:
committed by
Joost VandeVondele
parent
dc1686345c
commit
ab8f901d25
+2
-2
@@ -74,5 +74,5 @@ fetch_network() {
|
||||
return 1
|
||||
}
|
||||
|
||||
fetch_network EvalFileDefaultNameBig &&
|
||||
fetch_network EvalFileDefaultNameSmall
|
||||
fetch_network EvalFileDefaultName
|
||||
|
||||
|
||||
+23
-43
@@ -62,7 +62,7 @@ Engine::Engine(std::optional<std::string> path) :
|
||||
numaContext(NumaConfig::from_system(DefaultNumaPolicy)),
|
||||
states(new std::deque<StateInfo>(1)),
|
||||
threads(),
|
||||
networks(numaContext, get_default_networks()) {
|
||||
network(numaContext, get_default_network()) {
|
||||
|
||||
pos.set(StartFEN, false, &states->back());
|
||||
|
||||
@@ -132,14 +132,8 @@ Engine::Engine(std::optional<std::string> path) :
|
||||
options.add("SyzygyProbeLimit", Option(7, 0, 7));
|
||||
|
||||
options.add( //
|
||||
"EvalFile", Option(EvalFileDefaultNameBig, [this](const Option& o) {
|
||||
load_big_network(o);
|
||||
return std::nullopt;
|
||||
}));
|
||||
|
||||
options.add( //
|
||||
"EvalFileSmall", Option(EvalFileDefaultNameSmall, [this](const Option& o) {
|
||||
load_small_network(o);
|
||||
"EvalFile", Option(EvalFileDefaultName, [this](const Option& o) {
|
||||
load_network(o);
|
||||
return std::nullopt;
|
||||
}));
|
||||
|
||||
@@ -149,14 +143,14 @@ Engine::Engine(std::optional<std::string> path) :
|
||||
}
|
||||
|
||||
std::uint64_t Engine::perft(const std::string& fen, Depth depth, bool isChess960) {
|
||||
verify_networks();
|
||||
verify_network();
|
||||
|
||||
return Benchmark::perft(fen, depth, isChess960);
|
||||
}
|
||||
|
||||
void Engine::go(Search::LimitsType& limits) {
|
||||
assert(limits.perft == 0);
|
||||
verify_networks();
|
||||
verify_network();
|
||||
|
||||
threads.start_thinking(options, pos, states, limits);
|
||||
}
|
||||
@@ -188,8 +182,8 @@ void Engine::set_on_bestmove(std::function<void(std::string_view, std::string_vi
|
||||
updateContext.onBestmove = std::move(f);
|
||||
}
|
||||
|
||||
void Engine::set_on_verify_networks(std::function<void(std::string_view)>&& f) {
|
||||
onVerifyNetworks = std::move(f);
|
||||
void Engine::set_on_verify_network(std::function<void(std::string_view)>&& f) {
|
||||
onVerifyNetwork = std::move(f);
|
||||
}
|
||||
|
||||
void Engine::wait_for_search_finished() { threads.main_thread()->wait_for_search_finished(); }
|
||||
@@ -244,7 +238,7 @@ void Engine::set_numa_config_from_option(const std::string& o) {
|
||||
|
||||
void Engine::resize_threads() {
|
||||
threads.wait_for_search_finished();
|
||||
threads.set(numaContext.get_numa_config(), {options, threads, tt, sharedHists, networks},
|
||||
threads.set(numaContext.get_numa_config(), {options, threads, tt, sharedHists, network},
|
||||
updateContext);
|
||||
|
||||
// Reallocate the hash with the new threadpool size
|
||||
@@ -261,11 +255,10 @@ void Engine::set_ponderhit(bool b) { threads.main_manager()->ponder = b; }
|
||||
|
||||
// network related
|
||||
|
||||
void Engine::verify_networks() const {
|
||||
networks->big.verify(options["EvalFile"], onVerifyNetworks);
|
||||
networks->small.verify(options["EvalFileSmall"], onVerifyNetworks);
|
||||
void Engine::verify_network() const {
|
||||
network->verify(options["EvalFile"], onVerifyNetwork);
|
||||
|
||||
auto statuses = networks.get_status_and_errors();
|
||||
auto statuses = network.get_status_and_errors();
|
||||
for (size_t i = 0; i < statuses.size(); ++i)
|
||||
{
|
||||
const auto [status, error] = statuses[i];
|
||||
@@ -292,41 +285,28 @@ void Engine::verify_networks() const {
|
||||
message += " " + *error;
|
||||
}
|
||||
|
||||
onVerifyNetworks(message);
|
||||
onVerifyNetwork(message);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Eval::NNUE::Networks> Engine::get_default_networks() const {
|
||||
std::unique_ptr<Eval::NNUE::Network> Engine::get_default_network() const {
|
||||
|
||||
auto networks_ =
|
||||
std::make_unique<NN::Networks>(NN::EvalFile{EvalFileDefaultNameBig, "None", ""},
|
||||
NN::EvalFile{EvalFileDefaultNameSmall, "None", ""});
|
||||
auto network_ = std::make_unique<NN::Network>(NN::EvalFile{EvalFileDefaultName, "None", ""});
|
||||
|
||||
networks_->big.load(binaryDirectory, "");
|
||||
networks_->small.load(binaryDirectory, "");
|
||||
network_->load(binaryDirectory, "");
|
||||
|
||||
return networks_;
|
||||
return network_;
|
||||
}
|
||||
|
||||
void Engine::load_big_network(const std::string& file) {
|
||||
networks.modify_and_replicate(
|
||||
[this, &file](NN::Networks& networks_) { networks_.big.load(binaryDirectory, file); });
|
||||
void Engine::load_network(const std::string& file) {
|
||||
network.modify_and_replicate(
|
||||
[this, &file](NN::Network& network_) { network_.load(binaryDirectory, file); });
|
||||
threads.clear();
|
||||
threads.ensure_network_replicated();
|
||||
}
|
||||
|
||||
void Engine::load_small_network(const std::string& file) {
|
||||
networks.modify_and_replicate(
|
||||
[this, &file](NN::Networks& networks_) { networks_.small.load(binaryDirectory, file); });
|
||||
threads.clear();
|
||||
threads.ensure_network_replicated();
|
||||
}
|
||||
|
||||
void Engine::save_network(const std::pair<std::optional<std::string>, std::string> files[2]) {
|
||||
networks.modify_and_replicate([&files](NN::Networks& networks_) {
|
||||
networks_.big.save(files[0].first);
|
||||
networks_.small.save(files[1].first);
|
||||
});
|
||||
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); });
|
||||
}
|
||||
|
||||
// utility functions
|
||||
@@ -336,9 +316,9 @@ void Engine::trace_eval() const {
|
||||
Position p;
|
||||
p.set(pos.fen(), options["UCI_Chess960"], &trace_states->back());
|
||||
|
||||
verify_networks();
|
||||
verify_network();
|
||||
|
||||
sync_cout << "\n" << Eval::trace(p, *networks) << sync_endl;
|
||||
sync_cout << "\n" << Eval::trace(p, *network) << sync_endl;
|
||||
}
|
||||
|
||||
const OptionsMap& Engine::get_options() const { return options; }
|
||||
|
||||
+10
-11
@@ -83,15 +83,14 @@ class Engine {
|
||||
void set_on_update_full(std::function<void(const InfoFull&)>&&);
|
||||
void set_on_iter(std::function<void(const InfoIter&)>&&);
|
||||
void set_on_bestmove(std::function<void(std::string_view, std::string_view)>&&);
|
||||
void set_on_verify_networks(std::function<void(std::string_view)>&&);
|
||||
void set_on_verify_network(std::function<void(std::string_view)>&&);
|
||||
|
||||
// network related
|
||||
|
||||
void verify_networks() const;
|
||||
std::unique_ptr<Eval::NNUE::Networks> get_default_networks() const;
|
||||
void load_big_network(const std::string& file);
|
||||
void load_small_network(const std::string& file);
|
||||
void save_network(const std::pair<std::optional<std::string>, std::string> files[2]);
|
||||
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);
|
||||
|
||||
// utility functions
|
||||
|
||||
@@ -119,13 +118,13 @@ class Engine {
|
||||
Position pos;
|
||||
StateListPtr states;
|
||||
|
||||
OptionsMap options;
|
||||
ThreadPool threads;
|
||||
TranspositionTable tt;
|
||||
LazyNumaReplicatedSystemWide<Eval::NNUE::Networks> networks;
|
||||
OptionsMap options;
|
||||
ThreadPool threads;
|
||||
TranspositionTable tt;
|
||||
LazyNumaReplicatedSystemWide<Eval::NNUE::Network> network;
|
||||
|
||||
Search::SearchManager::UpdateContext updateContext;
|
||||
std::function<void(std::string_view)> onVerifyNetworks;
|
||||
std::function<void(std::string_view)> onVerifyNetwork;
|
||||
std::map<NumaIndex, SharedHistories> sharedHists;
|
||||
};
|
||||
|
||||
|
||||
+13
-36
@@ -26,7 +26,6 @@
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <tuple>
|
||||
|
||||
#include "nnue/network.h"
|
||||
#include "nnue/nnue_misc.h"
|
||||
@@ -46,11 +45,9 @@ int Eval::simple_eval(const Position& pos) {
|
||||
- pos.non_pawn_material(~c);
|
||||
}
|
||||
|
||||
bool Eval::use_smallnet(const Position& pos) { return std::abs(simple_eval(pos)) > 962; }
|
||||
|
||||
// Evaluate is the evaluator for the outer world. It returns a static evaluation
|
||||
// of the position from the point of view of the side to move.
|
||||
Value Eval::evaluate(const Eval::NNUE::Networks& networks,
|
||||
Value Eval::evaluate(const Eval::NNUE::Network& network,
|
||||
const Position& pos,
|
||||
Eval::NNUE::AccumulatorStack& accumulators,
|
||||
Eval::NNUE::AccumulatorCaches& caches,
|
||||
@@ -58,20 +55,10 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks,
|
||||
|
||||
assert(!pos.checkers());
|
||||
|
||||
bool smallNet = use_smallnet(pos);
|
||||
auto [psqt, positional] = smallNet ? networks.small.evaluate(pos, accumulators, caches.small)
|
||||
: networks.big.evaluate(pos, accumulators, caches.big);
|
||||
auto [psqt, positional] = network.evaluate(pos, accumulators, caches);
|
||||
|
||||
Value nnue = (125 * psqt + 131 * positional) / 128;
|
||||
|
||||
// Re-evaluate the position when higher eval accuracy is worth the time spent
|
||||
if (smallNet && (std::abs(nnue) < 277))
|
||||
{
|
||||
std::tie(psqt, positional) = networks.big.evaluate(pos, accumulators, caches.big);
|
||||
nnue = (125 * psqt + 131 * positional) / 128;
|
||||
smallNet = false;
|
||||
}
|
||||
|
||||
// Blend optimism and eval with nnue complexity
|
||||
int nnueComplexity = std::abs(psqt - positional);
|
||||
optimism += optimism * nnueComplexity / 476;
|
||||
@@ -93,43 +80,33 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks,
|
||||
// a string (suitable for outputting to stdout) that contains the detailed
|
||||
// descriptions and values of each evaluation term. Useful for debugging.
|
||||
// Trace scores are from white's point of view
|
||||
std::string Eval::trace(Position& pos, const Eval::NNUE::Networks& networks) {
|
||||
std::string Eval::trace(Position& pos, const Eval::NNUE::Network& network) {
|
||||
|
||||
if (pos.checkers())
|
||||
return "Final evaluation: none (in check)";
|
||||
|
||||
auto accumulators = std::make_unique<Eval::NNUE::AccumulatorStack>();
|
||||
auto caches = std::make_unique<Eval::NNUE::AccumulatorCaches>(networks);
|
||||
auto caches = std::make_unique<Eval::NNUE::AccumulatorCaches>(network);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2);
|
||||
ss << '\n' << NNUE::trace(pos, networks, *caches) << '\n';
|
||||
ss << '\n' << NNUE::trace(pos, network, *caches) << '\n';
|
||||
|
||||
ss << std::showpoint << std::showpos << std::fixed << std::setprecision(2) << std::setw(15);
|
||||
|
||||
auto [psqtSmall, positionalSmall] = networks.small.evaluate(pos, *accumulators, caches->small);
|
||||
Value vSmall = psqtSmall + positionalSmall;
|
||||
bool useBig = std::abs(vSmall) < 277;
|
||||
ss << "(Small net) NNUE evaluation " << vSmall << " (side to move, internal units)\n";
|
||||
vSmall = pos.side_to_move() == WHITE ? vSmall : -vSmall;
|
||||
ss << "(Small net) NNUE evaluation " << 0.01 * UCIEngine::to_cp(vSmall, pos)
|
||||
<< " (white side)\n";
|
||||
|
||||
auto [psqtBig, positionalBig] = networks.big.evaluate(pos, *accumulators, caches->big);
|
||||
Value vBig = psqtBig + positionalBig;
|
||||
ss << "(Big net) NNUE evaluation " << vBig << " (side to move, internal units)\n";
|
||||
vBig = pos.side_to_move() == WHITE ? vBig : -vBig;
|
||||
ss << "(Big net) NNUE evaluation " << 0.01 * UCIEngine::to_cp(vBig, pos)
|
||||
<< " (white side)\n";
|
||||
auto [psqt, positional] = network.evaluate(pos, *accumulators, *caches);
|
||||
Value v = psqt + positional;
|
||||
ss << "NNUE evaluation " << v << " (side to move, internal units)\n";
|
||||
v = pos.side_to_move() == WHITE ? v : -v;
|
||||
ss << "NNUE evaluation " << 0.01 * UCIEngine::to_cp(v, pos) << " (white side)\n";
|
||||
|
||||
ss << "SimpleEval " << simple_eval(pos)
|
||||
<< " (side to move, internal units)\n\n";
|
||||
|
||||
Value v = evaluate(networks, pos, *accumulators, *caches, VALUE_ZERO);
|
||||
v = pos.side_to_move() == WHITE ? v : -v;
|
||||
v = evaluate(network, pos, *accumulators, *caches, VALUE_ZERO);
|
||||
v = pos.side_to_move() == WHITE ? v : -v;
|
||||
|
||||
ss << "Final evaluation " << "(using "
|
||||
<< (use_smallnet(pos) && !useBig ? "small net) " : "big net) ");
|
||||
ss << "Final evaluation ";
|
||||
ss << 0.01 * UCIEngine::to_cp(v, pos) << " (white side)";
|
||||
ss << " [with scaled NNUE, ...]\n";
|
||||
|
||||
|
||||
+4
-5
@@ -33,20 +33,19 @@ namespace Eval {
|
||||
// for the build process (profile-build and fishtest) to work. Do not change the
|
||||
// name of the macro or the location where this macro is defined, as it is used
|
||||
// in the Makefile/Fishtest.
|
||||
#define EvalFileDefaultNameBig "nn-f68ec79f0fe3.nnue"
|
||||
#define EvalFileDefaultNameSmall "nn-47fc8b7fff06.nnue"
|
||||
#define EvalFileDefaultName "nn-f68ec79f0fe3.nnue"
|
||||
|
||||
namespace NNUE {
|
||||
struct Networks;
|
||||
class Network;
|
||||
struct AccumulatorCaches;
|
||||
class AccumulatorStack;
|
||||
}
|
||||
|
||||
std::string trace(Position& pos, const Eval::NNUE::Networks& networks);
|
||||
std::string trace(Position& pos, const Eval::NNUE::Network& network);
|
||||
|
||||
int simple_eval(const Position& pos);
|
||||
bool use_smallnet(const Position& pos);
|
||||
Value evaluate(const NNUE::Networks& networks,
|
||||
Value evaluate(const NNUE::Network& network,
|
||||
const Position& pos,
|
||||
Eval::NNUE::AccumulatorStack& accumulators,
|
||||
Eval::NNUE::AccumulatorCaches& caches,
|
||||
|
||||
+36
-104
@@ -44,52 +44,21 @@
|
||||
// const unsigned int gEmbeddedNNUESize; // the size of the embedded file
|
||||
// Note that this does not work in Microsoft Visual Studio.
|
||||
#if !defined(UNIVERSAL_BINARY) && !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF)
|
||||
INCBIN(EmbeddedNNUEBig, EvalFileDefaultNameBig);
|
||||
INCBIN(EmbeddedNNUESmall, EvalFileDefaultNameSmall);
|
||||
INCBIN(EmbeddedNNUE, EvalFileDefaultName);
|
||||
#elif defined(UNIVERSAL_BINARY)
|
||||
// When building for the universal binary, use C++26 #embed with weak symbols so that a
|
||||
// separate, non-LTO nnue_embed.o (with strong symbols) can override them during the LTO link,
|
||||
// (INCBIN can't deduplicate.)
|
||||
#define WEAK_SYM __attribute__((weak))
|
||||
extern const unsigned char gEmbeddedNNUEBigData[] WEAK_SYM = {
|
||||
#embed EvalFileDefaultNameBig
|
||||
extern const unsigned char gEmbeddedNNUEData[] WEAK_SYM = {
|
||||
#embed EvalFileDefaultName
|
||||
};
|
||||
extern const unsigned int gEmbeddedNNUEBigSize WEAK_SYM = sizeof(gEmbeddedNNUEBigData);
|
||||
extern const unsigned char gEmbeddedNNUESmallData[] WEAK_SYM = {
|
||||
#embed EvalFileDefaultNameSmall
|
||||
};
|
||||
extern const unsigned int gEmbeddedNNUESmallSize WEAK_SYM = sizeof(gEmbeddedNNUESmallData);
|
||||
extern const unsigned int gEmbeddedNNUESize WEAK_SYM = sizeof(gEmbeddedNNUEData);
|
||||
#else
|
||||
const unsigned char gEmbeddedNNUEBigData[1] = {0x0};
|
||||
const unsigned int gEmbeddedNNUEBigSize = 1;
|
||||
const unsigned char gEmbeddedNNUESmallData[1] = {0x0};
|
||||
const unsigned int gEmbeddedNNUESmallSize = 1;
|
||||
const unsigned char gEmbeddedNNUEData[1] = {0x0};
|
||||
const unsigned int gEmbeddedNNUESize = 1;
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
struct EmbeddedNNUE {
|
||||
EmbeddedNNUE(const unsigned char* embeddedData, const unsigned int embeddedSize) :
|
||||
data(embeddedData),
|
||||
end(embeddedData + embeddedSize),
|
||||
size(embeddedSize) {}
|
||||
const unsigned char* data;
|
||||
const unsigned char* end;
|
||||
const unsigned int size;
|
||||
};
|
||||
|
||||
using namespace Stockfish::Eval::NNUE;
|
||||
|
||||
EmbeddedNNUE get_embedded(EmbeddedNNUEType type) {
|
||||
if (type == EmbeddedNNUEType::BIG)
|
||||
return EmbeddedNNUE(gEmbeddedNNUEBigData, gEmbeddedNNUEBigSize);
|
||||
else
|
||||
return EmbeddedNNUE(gEmbeddedNNUESmallData, gEmbeddedNNUESmallSize);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
namespace Stockfish::Eval::NNUE {
|
||||
|
||||
|
||||
@@ -116,8 +85,7 @@ bool write_parameters(std::ostream& stream, const T& reference) {
|
||||
|
||||
} // namespace Detail
|
||||
|
||||
template<typename Arch, typename Transformer>
|
||||
void Network<Arch, Transformer>::load(const std::string& rootDirectory, std::string evalfilePath) {
|
||||
void Network::load(const std::string& rootDirectory, std::string evalfilePath) {
|
||||
#if defined(DEFAULT_NNUE_DIRECTORY)
|
||||
std::vector<std::string> dirs = {"<internal>", "", rootDirectory,
|
||||
stringify(DEFAULT_NNUE_DIRECTORY)};
|
||||
@@ -146,8 +114,7 @@ void Network<Arch, Transformer>::load(const std::string& rootDirectory, std::str
|
||||
}
|
||||
|
||||
|
||||
template<typename Arch, typename Transformer>
|
||||
bool Network<Arch, Transformer>::save(const std::optional<std::string>& filename) const {
|
||||
bool Network::save(const std::optional<std::string>& filename) const {
|
||||
std::string actualFilename;
|
||||
std::string msg;
|
||||
|
||||
@@ -177,16 +144,13 @@ bool Network<Arch, Transformer>::save(const std::optional<std::string>& filename
|
||||
}
|
||||
|
||||
|
||||
template<typename Arch, typename Transformer>
|
||||
NetworkOutput
|
||||
Network<Arch, Transformer>::evaluate(const Position& pos,
|
||||
AccumulatorStack& accumulatorStack,
|
||||
AccumulatorCaches::Cache<FTDimensions>& cache) const {
|
||||
NetworkOutput Network::evaluate(const Position& pos,
|
||||
AccumulatorStack& accumulatorStack,
|
||||
AccumulatorCaches& cache) const {
|
||||
|
||||
constexpr uint64_t alignment = CacheLineSize;
|
||||
|
||||
alignas(alignment)
|
||||
TransformedFeatureType transformedFeatures[FeatureTransformer<FTDimensions>::BufferSize];
|
||||
alignas(alignment) TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize];
|
||||
|
||||
ASSERT_ALIGNED(transformedFeatures, alignment);
|
||||
|
||||
@@ -198,9 +162,8 @@ Network<Arch, Transformer>::evaluate(const Position& pos
|
||||
}
|
||||
|
||||
|
||||
template<typename Arch, typename Transformer>
|
||||
void Network<Arch, Transformer>::verify(std::string evalfilePath,
|
||||
const std::function<void(std::string_view)>& f) const {
|
||||
void Network::verify(std::string evalfilePath,
|
||||
const std::function<void(std::string_view)>& f) const {
|
||||
if (evalfilePath.empty())
|
||||
evalfilePath = evalFile.defaultName;
|
||||
|
||||
@@ -229,9 +192,9 @@ void Network<Arch, Transformer>::verify(std::string
|
||||
|
||||
if (f)
|
||||
{
|
||||
size_t size = sizeof(featureTransformer) + sizeof(Arch) * LayerStacks;
|
||||
size_t size = sizeof(featureTransformer) + sizeof(NetworkArchitecture) * LayerStacks;
|
||||
f("NNUE evaluation using " + evalfilePath + " (" + std::to_string(size / (1024 * 1024))
|
||||
+ "MiB, (" + std::to_string(featureTransformer.TotalInputDimensions) + ", "
|
||||
+ "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))");
|
||||
@@ -239,16 +202,13 @@ void Network<Arch, Transformer>::verify(std::string
|
||||
}
|
||||
|
||||
|
||||
template<typename Arch, typename Transformer>
|
||||
NnueEvalTrace
|
||||
Network<Arch, Transformer>::trace_evaluate(const Position& pos,
|
||||
AccumulatorStack& accumulatorStack,
|
||||
AccumulatorCaches::Cache<FTDimensions>& cache) const {
|
||||
NnueEvalTrace Network::trace_evaluate(const Position& pos,
|
||||
AccumulatorStack& accumulatorStack,
|
||||
AccumulatorCaches& cache) const {
|
||||
|
||||
constexpr uint64_t alignment = CacheLineSize;
|
||||
|
||||
alignas(alignment)
|
||||
TransformedFeatureType transformedFeatures[FeatureTransformer<FTDimensions>::BufferSize];
|
||||
alignas(alignment) TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize];
|
||||
|
||||
ASSERT_ALIGNED(transformedFeatures, alignment);
|
||||
|
||||
@@ -268,9 +228,7 @@ Network<Arch, Transformer>::trace_evaluate(const Position&
|
||||
}
|
||||
|
||||
|
||||
template<typename Arch, typename Transformer>
|
||||
void Network<Arch, Transformer>::load_user_net(const std::string& dir,
|
||||
const std::string& evalfilePath) {
|
||||
void Network::load_user_net(const std::string& dir, const std::string& evalfilePath) {
|
||||
std::ifstream stream(dir + evalfilePath, std::ios::binary);
|
||||
auto description = load(stream);
|
||||
|
||||
@@ -282,8 +240,7 @@ void Network<Arch, Transformer>::load_user_net(const std::string& dir,
|
||||
}
|
||||
|
||||
|
||||
template<typename Arch, typename Transformer>
|
||||
void Network<Arch, Transformer>::load_internal() {
|
||||
void Network::load_internal() {
|
||||
// C++ way to prepare a buffer for a memory stream
|
||||
class MemoryBuffer: public std::basic_streambuf<char> {
|
||||
public:
|
||||
@@ -293,10 +250,8 @@ void Network<Arch, Transformer>::load_internal() {
|
||||
}
|
||||
};
|
||||
|
||||
const auto embedded = get_embedded(embeddedType);
|
||||
|
||||
MemoryBuffer buffer(const_cast<char*>(reinterpret_cast<const char*>(embedded.data)),
|
||||
size_t(embedded.size));
|
||||
MemoryBuffer buffer(const_cast<char*>(reinterpret_cast<const char*>(gEmbeddedNNUEData)),
|
||||
size_t(gEmbeddedNNUESize));
|
||||
|
||||
std::istream stream(&buffer);
|
||||
auto description = load(stream);
|
||||
@@ -309,16 +264,12 @@ void Network<Arch, Transformer>::load_internal() {
|
||||
}
|
||||
|
||||
|
||||
template<typename Arch, typename Transformer>
|
||||
void Network<Arch, Transformer>::initialize() {
|
||||
initialized = true;
|
||||
}
|
||||
void Network::initialize() { initialized = true; }
|
||||
|
||||
|
||||
template<typename Arch, typename Transformer>
|
||||
bool Network<Arch, Transformer>::save(std::ostream& stream,
|
||||
const std::string& name,
|
||||
const std::string& netDescription) const {
|
||||
bool Network::save(std::ostream& stream,
|
||||
const std::string& name,
|
||||
const std::string& netDescription) const {
|
||||
if (name.empty() || name == "None")
|
||||
return false;
|
||||
|
||||
@@ -326,8 +277,7 @@ bool Network<Arch, Transformer>::save(std::ostream& stream,
|
||||
}
|
||||
|
||||
|
||||
template<typename Arch, typename Transformer>
|
||||
std::optional<std::string> Network<Arch, Transformer>::load(std::istream& stream) {
|
||||
std::optional<std::string> Network::load(std::istream& stream) {
|
||||
initialize();
|
||||
std::string description;
|
||||
|
||||
@@ -335,8 +285,7 @@ std::optional<std::string> Network<Arch, Transformer>::load(std::istream& stream
|
||||
}
|
||||
|
||||
|
||||
template<typename Arch, typename Transformer>
|
||||
std::size_t Network<Arch, Transformer>::get_content_hash() const {
|
||||
std::size_t Network::get_content_hash() const {
|
||||
if (!initialized)
|
||||
return 0;
|
||||
|
||||
@@ -345,15 +294,11 @@ std::size_t Network<Arch, Transformer>::get_content_hash() const {
|
||||
for (auto&& layerstack : network)
|
||||
hash_combine(h, layerstack);
|
||||
hash_combine(h, evalFile);
|
||||
hash_combine(h, static_cast<int>(embeddedType));
|
||||
return h;
|
||||
}
|
||||
|
||||
// Read network header
|
||||
template<typename Arch, typename Transformer>
|
||||
bool Network<Arch, Transformer>::read_header(std::istream& stream,
|
||||
std::uint32_t* hashValue,
|
||||
std::string* desc) const {
|
||||
bool Network::read_header(std::istream& stream, std::uint32_t* hashValue, std::string* desc) const {
|
||||
std::uint32_t version, size;
|
||||
|
||||
version = read_little_endian<std::uint32_t>(stream);
|
||||
@@ -368,10 +313,9 @@ bool Network<Arch, Transformer>::read_header(std::istream& stream,
|
||||
|
||||
|
||||
// Write network header
|
||||
template<typename Arch, typename Transformer>
|
||||
bool Network<Arch, Transformer>::write_header(std::ostream& stream,
|
||||
std::uint32_t hashValue,
|
||||
const std::string& desc) const {
|
||||
bool Network::write_header(std::ostream& stream,
|
||||
std::uint32_t hashValue,
|
||||
const std::string& desc) const {
|
||||
write_little_endian<std::uint32_t>(stream, Version);
|
||||
write_little_endian<std::uint32_t>(stream, hashValue);
|
||||
write_little_endian<std::uint32_t>(stream, std::uint32_t(desc.size()));
|
||||
@@ -380,9 +324,7 @@ bool Network<Arch, Transformer>::write_header(std::ostream& stream,
|
||||
}
|
||||
|
||||
|
||||
template<typename Arch, typename Transformer>
|
||||
bool Network<Arch, Transformer>::read_parameters(std::istream& stream,
|
||||
std::string& netDescription) {
|
||||
bool Network::read_parameters(std::istream& stream, std::string& netDescription) {
|
||||
std::uint32_t hashValue;
|
||||
if (!read_header(stream, &hashValue, &netDescription))
|
||||
return false;
|
||||
@@ -399,9 +341,7 @@ bool Network<Arch, Transformer>::read_parameters(std::istream& stream,
|
||||
}
|
||||
|
||||
|
||||
template<typename Arch, typename Transformer>
|
||||
bool Network<Arch, Transformer>::write_parameters(std::ostream& stream,
|
||||
const std::string& netDescription) const {
|
||||
bool Network::write_parameters(std::ostream& stream, const std::string& netDescription) const {
|
||||
if (!write_header(stream, Network::hash, netDescription))
|
||||
return false;
|
||||
if (!Detail::write_parameters(stream, featureTransformer))
|
||||
@@ -414,12 +354,4 @@ bool Network<Arch, Transformer>::write_parameters(std::ostream& stream,
|
||||
return bool(stream);
|
||||
}
|
||||
|
||||
// Explicit template instantiations
|
||||
|
||||
template class Network<NetworkArchitecture<TransformedFeatureDimensionsBig, L2Big, L3Big>,
|
||||
FeatureTransformer<TransformedFeatureDimensionsBig>>;
|
||||
|
||||
template class Network<NetworkArchitecture<TransformedFeatureDimensionsSmall, L2Small, L3Small>,
|
||||
FeatureTransformer<TransformedFeatureDimensionsSmall>>;
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE
|
||||
|
||||
+19
-61
@@ -29,11 +29,8 @@
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
|
||||
#include "../misc.h"
|
||||
#include "../types.h"
|
||||
#include "nnue_accumulator.h"
|
||||
#include "nnue_architecture.h"
|
||||
#include "nnue_common.h"
|
||||
#include "nnue_feature_transformer.h"
|
||||
#include "nnue_misc.h"
|
||||
|
||||
@@ -43,24 +40,18 @@ class Position;
|
||||
|
||||
namespace Stockfish::Eval::NNUE {
|
||||
|
||||
enum class EmbeddedNNUEType {
|
||||
BIG,
|
||||
SMALL,
|
||||
};
|
||||
class AccumulatorStack;
|
||||
struct AccumulatorCaches;
|
||||
|
||||
using NetworkOutput = std::tuple<Value, Value>;
|
||||
|
||||
// The network must be a trivial type, i.e. the memory must be in-line.
|
||||
// This is required to allow sharing the network via shared memory, as
|
||||
// there is no way to run destructors.
|
||||
template<typename Arch, typename Transformer>
|
||||
class Network {
|
||||
static constexpr IndexType FTDimensions = Arch::TransformedFeatureDimensions;
|
||||
|
||||
public:
|
||||
Network(EvalFile file, EmbeddedNNUEType type) :
|
||||
evalFile(file),
|
||||
embeddedType(type) {}
|
||||
Network(EvalFile file) :
|
||||
evalFile(file) {}
|
||||
|
||||
Network(const Network& other) = default;
|
||||
Network(Network&& other) = default;
|
||||
@@ -73,15 +64,15 @@ class Network {
|
||||
|
||||
std::size_t get_content_hash() const;
|
||||
|
||||
NetworkOutput evaluate(const Position& pos,
|
||||
AccumulatorStack& accumulatorStack,
|
||||
AccumulatorCaches::Cache<FTDimensions>& cache) const;
|
||||
NetworkOutput evaluate(const Position& pos,
|
||||
AccumulatorStack& accumulatorStack,
|
||||
AccumulatorCaches& cache) const;
|
||||
|
||||
|
||||
void verify(std::string evalfilePath, const std::function<void(std::string_view)>&) const;
|
||||
NnueEvalTrace trace_evaluate(const Position& pos,
|
||||
AccumulatorStack& accumulatorStack,
|
||||
AccumulatorCaches::Cache<FTDimensions>& cache) const;
|
||||
NnueEvalTrace trace_evaluate(const Position& pos,
|
||||
AccumulatorStack& accumulatorStack,
|
||||
AccumulatorCaches& cache) const;
|
||||
|
||||
private:
|
||||
void load_user_net(const std::string&, const std::string&);
|
||||
@@ -99,63 +90,30 @@ class Network {
|
||||
bool write_parameters(std::ostream&, const std::string&) const;
|
||||
|
||||
// Input feature converter
|
||||
Transformer featureTransformer;
|
||||
FeatureTransformer featureTransformer;
|
||||
|
||||
// Evaluation function
|
||||
Arch network[LayerStacks];
|
||||
NetworkArchitecture network[LayerStacks];
|
||||
|
||||
EvalFile evalFile;
|
||||
EmbeddedNNUEType embeddedType;
|
||||
EvalFile evalFile;
|
||||
|
||||
bool initialized = false;
|
||||
|
||||
// Hash value of evaluation function structure
|
||||
static constexpr std::uint32_t hash = Transformer::get_hash_value() ^ Arch::get_hash_value();
|
||||
static constexpr std::uint32_t hash =
|
||||
FeatureTransformer::get_hash_value() ^ NetworkArchitecture::get_hash_value();
|
||||
|
||||
template<IndexType Size>
|
||||
friend struct AccumulatorCaches::Cache;
|
||||
};
|
||||
|
||||
// Definitions of the network types
|
||||
using SmallFeatureTransformer = FeatureTransformer<TransformedFeatureDimensionsSmall>;
|
||||
using SmallNetworkArchitecture =
|
||||
NetworkArchitecture<TransformedFeatureDimensionsSmall, L2Small, L3Small>;
|
||||
|
||||
using BigFeatureTransformer = FeatureTransformer<TransformedFeatureDimensionsBig>;
|
||||
using BigNetworkArchitecture = NetworkArchitecture<TransformedFeatureDimensionsBig, L2Big, L3Big>;
|
||||
|
||||
using NetworkBig = Network<BigNetworkArchitecture, BigFeatureTransformer>;
|
||||
using NetworkSmall = Network<SmallNetworkArchitecture, SmallFeatureTransformer>;
|
||||
|
||||
|
||||
struct Networks {
|
||||
Networks(EvalFile bigFile, EvalFile smallFile) :
|
||||
big(bigFile, EmbeddedNNUEType::BIG),
|
||||
small(smallFile, EmbeddedNNUEType::SMALL) {}
|
||||
|
||||
NetworkBig big;
|
||||
NetworkSmall small;
|
||||
friend struct AccumulatorCaches;
|
||||
};
|
||||
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
template<typename ArchT, typename FeatureTransformerT>
|
||||
struct std::hash<Stockfish::Eval::NNUE::Network<ArchT, FeatureTransformerT>> {
|
||||
std::size_t operator()(
|
||||
const Stockfish::Eval::NNUE::Network<ArchT, FeatureTransformerT>& network) const noexcept {
|
||||
template<>
|
||||
struct std::hash<Stockfish::Eval::NNUE::Network> {
|
||||
std::size_t operator()(const Stockfish::Eval::NNUE::Network& network) const noexcept {
|
||||
return network.get_content_hash();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct std::hash<Stockfish::Eval::NNUE::Networks> {
|
||||
std::size_t operator()(const Stockfish::Eval::NNUE::Networks& networks) const noexcept {
|
||||
std::size_t h = 0;
|
||||
Stockfish::hash_combine(h, networks.big);
|
||||
Stockfish::hash_combine(h, networks.small);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+95
-118
@@ -38,26 +38,23 @@ using namespace SIMD;
|
||||
|
||||
namespace {
|
||||
|
||||
template<bool Forward, typename FeatureSet, IndexType TransformedFeatureDimensions>
|
||||
void update_accumulator_incremental(
|
||||
Color perspective,
|
||||
const FeatureTransformer<TransformedFeatureDimensions>& featureTransformer,
|
||||
const Square ksq,
|
||||
AccumulatorState<FeatureSet>& target_state,
|
||||
const AccumulatorState<FeatureSet>& computed);
|
||||
template<bool Forward, typename FeatureSet>
|
||||
void update_accumulator_incremental(Color perspective,
|
||||
const FeatureTransformer& featureTransformer,
|
||||
const Square ksq,
|
||||
AccumulatorState<FeatureSet>& target_state,
|
||||
const AccumulatorState<FeatureSet>& computed);
|
||||
|
||||
template<IndexType Dimensions>
|
||||
void update_accumulator_refresh_cache(Color perspective,
|
||||
const FeatureTransformer<Dimensions>& featureTransformer,
|
||||
const Position& pos,
|
||||
AccumulatorState<PSQFeatureSet>& accumulatorState,
|
||||
AccumulatorCaches::Cache<Dimensions>& cache);
|
||||
void update_accumulator_refresh_cache(Color perspective,
|
||||
const FeatureTransformer& featureTransformer,
|
||||
const Position& pos,
|
||||
AccumulatorState<PSQFeatureSet>& accumulatorState,
|
||||
AccumulatorCaches& cache);
|
||||
|
||||
template<IndexType Dimensions>
|
||||
void update_threats_accumulator_full(Color perspective,
|
||||
const FeatureTransformer<Dimensions>& featureTransformer,
|
||||
const Position& pos,
|
||||
AccumulatorState<ThreatFeatureSet>& accumulatorState);
|
||||
void update_threats_accumulator_full(Color perspective,
|
||||
const FeatureTransformer& featureTransformer,
|
||||
const Position& pos,
|
||||
AccumulatorState<ThreatFeatureSet>& accumulatorState);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -120,33 +117,26 @@ void AccumulatorStack::pop() noexcept {
|
||||
size--;
|
||||
}
|
||||
|
||||
template<IndexType Dimensions>
|
||||
void AccumulatorStack::evaluate(const Position& pos,
|
||||
const FeatureTransformer<Dimensions>& featureTransformer,
|
||||
AccumulatorCaches::Cache<Dimensions>& cache) noexcept {
|
||||
constexpr bool UseThreats = (Dimensions == TransformedFeatureDimensionsBig);
|
||||
|
||||
void AccumulatorStack::evaluate(const Position& pos,
|
||||
const FeatureTransformer& featureTransformer,
|
||||
// Silence spurious warning on GCC 10
|
||||
[[maybe_unused]] AccumulatorCaches& cache) noexcept {
|
||||
evaluate_side<PSQFeatureSet>(WHITE, pos, featureTransformer, cache);
|
||||
evaluate_side<PSQFeatureSet>(BLACK, pos, featureTransformer, cache);
|
||||
|
||||
if (UseThreats)
|
||||
{
|
||||
evaluate_side<ThreatFeatureSet>(WHITE, pos, featureTransformer, cache);
|
||||
evaluate_side<ThreatFeatureSet>(BLACK, pos, featureTransformer, cache);
|
||||
}
|
||||
evaluate_side<ThreatFeatureSet>(WHITE, pos, featureTransformer, cache);
|
||||
evaluate_side<ThreatFeatureSet>(BLACK, pos, featureTransformer, cache);
|
||||
}
|
||||
|
||||
template<typename FeatureSet, IndexType Dimensions>
|
||||
void AccumulatorStack::evaluate_side(Color perspective,
|
||||
const Position& pos,
|
||||
const FeatureTransformer<Dimensions>& featureTransformer,
|
||||
AccumulatorCaches::Cache<Dimensions>& cache) noexcept {
|
||||
template<typename FeatureSet>
|
||||
void AccumulatorStack::evaluate_side(Color perspective,
|
||||
const Position& pos,
|
||||
const FeatureTransformer& featureTransformer,
|
||||
AccumulatorCaches& cache) noexcept {
|
||||
|
||||
const auto last_usable_accum =
|
||||
find_last_usable_accumulator<FeatureSet, Dimensions>(perspective);
|
||||
const auto last_usable_accum = find_last_usable_accumulator<FeatureSet>(perspective);
|
||||
|
||||
if ((accumulators<FeatureSet>()[last_usable_accum].template acc<Dimensions>())
|
||||
.computed[perspective])
|
||||
if (accumulators<FeatureSet>()[last_usable_accum].computed[perspective])
|
||||
forward_update_incremental<FeatureSet>(perspective, pos, featureTransformer,
|
||||
last_usable_accum);
|
||||
|
||||
@@ -166,12 +156,12 @@ void AccumulatorStack::evaluate_side(Color persp
|
||||
|
||||
// Find the earliest usable accumulator, this can either be a computed accumulator or the accumulator
|
||||
// state just before a change that requires full refresh.
|
||||
template<typename FeatureSet, IndexType Dimensions>
|
||||
template<typename FeatureSet>
|
||||
std::size_t AccumulatorStack::find_last_usable_accumulator(Color perspective) const noexcept {
|
||||
|
||||
for (std::size_t curr_idx = size - 1; curr_idx > 0; curr_idx--)
|
||||
{
|
||||
if ((accumulators<FeatureSet>()[curr_idx].template acc<Dimensions>()).computed[perspective])
|
||||
if (accumulators<FeatureSet>()[curr_idx].computed[perspective])
|
||||
return curr_idx;
|
||||
|
||||
if (FeatureSet::requires_refresh(accumulators<FeatureSet>()[curr_idx].diff, perspective))
|
||||
@@ -181,15 +171,14 @@ std::size_t AccumulatorStack::find_last_usable_accumulator(Color perspective) co
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename FeatureSet, IndexType Dimensions>
|
||||
void AccumulatorStack::forward_update_incremental(
|
||||
Color perspective,
|
||||
const Position& pos,
|
||||
const FeatureTransformer<Dimensions>& featureTransformer,
|
||||
const std::size_t begin) noexcept {
|
||||
template<typename FeatureSet>
|
||||
void AccumulatorStack::forward_update_incremental(Color perspective,
|
||||
const Position& pos,
|
||||
const FeatureTransformer& featureTransformer,
|
||||
const std::size_t begin) noexcept {
|
||||
|
||||
assert(begin < accumulators<FeatureSet>().size());
|
||||
assert((accumulators<FeatureSet>()[begin].template acc<Dimensions>()).computed[perspective]);
|
||||
assert(accumulators<FeatureSet>()[begin].computed[perspective]);
|
||||
|
||||
const Square ksq = pos.square<KING>(perspective);
|
||||
|
||||
@@ -200,20 +189,19 @@ void AccumulatorStack::forward_update_incremental(
|
||||
accumulators<FeatureSet>()[next - 1]);
|
||||
}
|
||||
|
||||
assert((latest<PSQFeatureSet>().acc<Dimensions>()).computed[perspective]);
|
||||
assert(latest<FeatureSet>().computed[perspective]);
|
||||
}
|
||||
|
||||
template<typename FeatureSet, IndexType Dimensions>
|
||||
void AccumulatorStack::backward_update_incremental(
|
||||
Color perspective,
|
||||
template<typename FeatureSet>
|
||||
void AccumulatorStack::backward_update_incremental(Color perspective,
|
||||
|
||||
const Position& pos,
|
||||
const FeatureTransformer<Dimensions>& featureTransformer,
|
||||
const std::size_t end) noexcept {
|
||||
const Position& pos,
|
||||
const FeatureTransformer& featureTransformer,
|
||||
const std::size_t end) noexcept {
|
||||
|
||||
assert(end < accumulators<FeatureSet>().size());
|
||||
assert(end < size);
|
||||
assert((latest<FeatureSet>().template acc<Dimensions>()).computed[perspective]);
|
||||
assert(latest<FeatureSet>().computed[perspective]);
|
||||
|
||||
const Square ksq = pos.square<KING>(perspective);
|
||||
|
||||
@@ -222,20 +210,9 @@ void AccumulatorStack::backward_update_incremental(
|
||||
mut_accumulators<FeatureSet>()[next],
|
||||
accumulators<FeatureSet>()[next + 1]);
|
||||
|
||||
assert((accumulators<FeatureSet>()[end].template acc<Dimensions>()).computed[perspective]);
|
||||
assert(accumulators<FeatureSet>()[end].computed[perspective]);
|
||||
}
|
||||
|
||||
// Explicit template instantiations
|
||||
template void AccumulatorStack::evaluate<TransformedFeatureDimensionsBig>(
|
||||
const Position& pos,
|
||||
const FeatureTransformer<TransformedFeatureDimensionsBig>& featureTransformer,
|
||||
AccumulatorCaches::Cache<TransformedFeatureDimensionsBig>& cache) noexcept;
|
||||
template void AccumulatorStack::evaluate<TransformedFeatureDimensionsSmall>(
|
||||
const Position& pos,
|
||||
const FeatureTransformer<TransformedFeatureDimensionsSmall>& featureTransformer,
|
||||
AccumulatorCaches::Cache<TransformedFeatureDimensionsSmall>& cache) noexcept;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename VectorWrapper,
|
||||
@@ -255,17 +232,17 @@ void fused_row_reduce(const ElementType* in, ElementType* out, const Ts* const..
|
||||
vecIn[i], reinterpret_cast<const typename VectorWrapper::type*>(rows)[i]...);
|
||||
}
|
||||
|
||||
template<typename FeatureSet, IndexType Dimensions>
|
||||
template<typename FeatureSet>
|
||||
struct AccumulatorUpdateContext {
|
||||
Color perspective;
|
||||
const FeatureTransformer<Dimensions>& featureTransformer;
|
||||
const AccumulatorState<FeatureSet>& from;
|
||||
AccumulatorState<FeatureSet>& to;
|
||||
Color perspective;
|
||||
const FeatureTransformer& featureTransformer;
|
||||
const AccumulatorState<FeatureSet>& from;
|
||||
AccumulatorState<FeatureSet>& to;
|
||||
|
||||
AccumulatorUpdateContext(Color persp,
|
||||
const FeatureTransformer<Dimensions>& ft,
|
||||
const AccumulatorState<FeatureSet>& accF,
|
||||
AccumulatorState<FeatureSet>& accT) noexcept :
|
||||
AccumulatorUpdateContext(Color persp,
|
||||
const FeatureTransformer& ft,
|
||||
const AccumulatorState<FeatureSet>& accF,
|
||||
AccumulatorState<FeatureSet>& accT) noexcept :
|
||||
perspective{persp},
|
||||
featureTransformer{ft},
|
||||
from{accF},
|
||||
@@ -275,6 +252,8 @@ struct AccumulatorUpdateContext {
|
||||
typename... Ts,
|
||||
std::enable_if_t<is_all_same_v<IndexType, Ts...>, bool> = true>
|
||||
void apply(const Ts... indices) {
|
||||
constexpr IndexType Dimensions = FeatureTransformer::OutputDimensions;
|
||||
|
||||
auto to_weight_vector = [&](const IndexType index) {
|
||||
return &featureTransformer.weights[index * Dimensions];
|
||||
};
|
||||
@@ -283,27 +262,28 @@ struct AccumulatorUpdateContext {
|
||||
return &featureTransformer.psqtWeights[index * PSQTBuckets];
|
||||
};
|
||||
|
||||
fused_row_reduce<Vec16Wrapper, Dimensions, ops...>(
|
||||
(from.template acc<Dimensions>()).accumulation[perspective].data(),
|
||||
(to.template acc<Dimensions>()).accumulation[perspective].data(),
|
||||
to_weight_vector(indices)...);
|
||||
fused_row_reduce<Vec16Wrapper, Dimensions, ops...>(from.accumulation[perspective].data(),
|
||||
to.accumulation[perspective].data(),
|
||||
to_weight_vector(indices)...);
|
||||
|
||||
fused_row_reduce<Vec32Wrapper, PSQTBuckets, ops...>(
|
||||
(from.template acc<Dimensions>()).psqtAccumulation[perspective].data(),
|
||||
(to.template acc<Dimensions>()).psqtAccumulation[perspective].data(),
|
||||
from.psqtAccumulation[perspective].data(), to.psqtAccumulation[perspective].data(),
|
||||
to_psqt_weight_vector(indices)...);
|
||||
}
|
||||
|
||||
void apply(const typename FeatureSet::IndexList& added,
|
||||
const typename FeatureSet::IndexList& removed) {
|
||||
const auto& fromAcc = from.template acc<Dimensions>().accumulation[perspective];
|
||||
auto& toAcc = to.template acc<Dimensions>().accumulation[perspective];
|
||||
constexpr IndexType Dimensions = FeatureTransformer::OutputDimensions;
|
||||
|
||||
const auto& fromPsqtAcc = from.template acc<Dimensions>().psqtAccumulation[perspective];
|
||||
auto& toPsqtAcc = to.template acc<Dimensions>().psqtAccumulation[perspective];
|
||||
const auto& fromAcc = from.accumulation[perspective];
|
||||
auto& toAcc = to.accumulation[perspective];
|
||||
|
||||
const auto& fromPsqtAcc = from.psqtAccumulation[perspective];
|
||||
auto& toPsqtAcc = to.psqtAccumulation[perspective];
|
||||
|
||||
#ifdef VECTOR
|
||||
using Tiling = SIMDTiling<Dimensions, Dimensions, PSQTBuckets>;
|
||||
|
||||
vec_t acc[Tiling::NumRegs];
|
||||
psqt_vec_t psqt[Tiling::NumPsqtRegs];
|
||||
|
||||
@@ -426,25 +406,24 @@ struct AccumulatorUpdateContext {
|
||||
}
|
||||
};
|
||||
|
||||
template<typename FeatureSet, IndexType Dimensions>
|
||||
auto make_accumulator_update_context(Color perspective,
|
||||
const FeatureTransformer<Dimensions>& featureTransformer,
|
||||
const AccumulatorState<FeatureSet>& accumulatorFrom,
|
||||
AccumulatorState<FeatureSet>& accumulatorTo) noexcept {
|
||||
return AccumulatorUpdateContext<FeatureSet, Dimensions>{perspective, featureTransformer,
|
||||
accumulatorFrom, accumulatorTo};
|
||||
template<typename FeatureSet>
|
||||
auto make_accumulator_update_context(Color perspective,
|
||||
const FeatureTransformer& featureTransformer,
|
||||
const AccumulatorState<FeatureSet>& accumulatorFrom,
|
||||
AccumulatorState<FeatureSet>& accumulatorTo) noexcept {
|
||||
return AccumulatorUpdateContext<FeatureSet>{perspective, featureTransformer, accumulatorFrom,
|
||||
accumulatorTo};
|
||||
}
|
||||
|
||||
template<bool Forward, typename FeatureSet, IndexType TransformedFeatureDimensions>
|
||||
void update_accumulator_incremental(
|
||||
Color perspective,
|
||||
const FeatureTransformer<TransformedFeatureDimensions>& featureTransformer,
|
||||
const Square ksq,
|
||||
AccumulatorState<FeatureSet>& target_state,
|
||||
const AccumulatorState<FeatureSet>& computed) {
|
||||
template<bool Forward, typename FeatureSet>
|
||||
void update_accumulator_incremental(Color perspective,
|
||||
const FeatureTransformer& featureTransformer,
|
||||
const Square ksq,
|
||||
AccumulatorState<FeatureSet>& target_state,
|
||||
const AccumulatorState<FeatureSet>& computed) {
|
||||
|
||||
assert((computed.template acc<TransformedFeatureDimensions>()).computed[perspective]);
|
||||
assert(!(target_state.template acc<TransformedFeatureDimensions>()).computed[perspective]);
|
||||
assert(computed.computed[perspective]);
|
||||
assert(!target_state.computed[perspective]);
|
||||
|
||||
// The size must be enough to contain the largest possible update.
|
||||
// That might depend on the feature set and generally relies on the
|
||||
@@ -456,7 +435,7 @@ void update_accumulator_incremental(
|
||||
if constexpr (std::is_same_v<FeatureSet, ThreatFeatureSet>)
|
||||
{
|
||||
const auto* pfBase = &featureTransformer.threatWeights[0];
|
||||
auto pfStride = static_cast<IndexType>(TransformedFeatureDimensions);
|
||||
IndexType pfStride = FeatureTransformer::OutputDimensions;
|
||||
if constexpr (Forward)
|
||||
FeatureSet::append_changed_indices(perspective, ksq, target_state.diff, removed, added,
|
||||
nullptr, false, pfBase, pfStride);
|
||||
@@ -519,7 +498,7 @@ void update_accumulator_incremental(
|
||||
}
|
||||
}
|
||||
|
||||
(target_state.template acc<TransformedFeatureDimensions>()).computed[perspective] = true;
|
||||
target_state.computed[perspective] = true;
|
||||
}
|
||||
|
||||
Bitboard get_changed_pieces(const std::array<Piece, SQUARE_NB>& oldPieces,
|
||||
@@ -559,12 +538,12 @@ Bitboard get_changed_pieces(const std::array<Piece, SQUARE_NB>& oldPieces,
|
||||
#endif
|
||||
}
|
||||
|
||||
template<IndexType Dimensions>
|
||||
void update_accumulator_refresh_cache(Color perspective,
|
||||
const FeatureTransformer<Dimensions>& featureTransformer,
|
||||
const Position& pos,
|
||||
AccumulatorState<PSQFeatureSet>& accumulatorState,
|
||||
AccumulatorCaches::Cache<Dimensions>& cache) {
|
||||
void update_accumulator_refresh_cache(Color perspective,
|
||||
const FeatureTransformer& featureTransformer,
|
||||
const Position& pos,
|
||||
AccumulatorState<PSQFeatureSet>& accumulator,
|
||||
AccumulatorCaches& cache) {
|
||||
constexpr auto Dimensions = FeatureTransformer::OutputDimensions;
|
||||
|
||||
using Tiling [[maybe_unused]] = SIMDTiling<Dimensions, Dimensions, PSQTBuckets>;
|
||||
|
||||
@@ -595,7 +574,6 @@ void update_accumulator_refresh_cache(Color pers
|
||||
entry.pieceBB = pos.pieces();
|
||||
entry.pieces = pos.piece_array();
|
||||
|
||||
auto& accumulator = accumulatorState.acc<Dimensions>();
|
||||
accumulator.computed[perspective] = true;
|
||||
|
||||
#ifdef VECTOR
|
||||
@@ -705,17 +683,16 @@ void update_accumulator_refresh_cache(Color pers
|
||||
#endif
|
||||
}
|
||||
|
||||
template<IndexType Dimensions>
|
||||
void update_threats_accumulator_full(Color perspective,
|
||||
const FeatureTransformer<Dimensions>& featureTransformer,
|
||||
const Position& pos,
|
||||
AccumulatorState<ThreatFeatureSet>& accumulatorState) {
|
||||
using Tiling [[maybe_unused]] = SIMDTiling<Dimensions, Dimensions, PSQTBuckets>;
|
||||
void update_threats_accumulator_full(Color perspective,
|
||||
const FeatureTransformer& featureTransformer,
|
||||
const Position& pos,
|
||||
AccumulatorState<ThreatFeatureSet>& accumulator) {
|
||||
constexpr IndexType Dimensions = FeatureTransformer::OutputDimensions;
|
||||
using Tiling [[maybe_unused]] = SIMDTiling<Dimensions, Dimensions, PSQTBuckets>;
|
||||
|
||||
ThreatFeatureSet::IndexList active;
|
||||
ThreatFeatureSet::append_active_indices(perspective, pos, active);
|
||||
|
||||
auto& accumulator = accumulatorState.acc<Dimensions>();
|
||||
accumulator.computed[perspective] = true;
|
||||
|
||||
#ifdef VECTOR
|
||||
|
||||
+48
-92
@@ -37,16 +37,13 @@ class Position;
|
||||
|
||||
namespace Stockfish::Eval::NNUE {
|
||||
|
||||
template<IndexType Size>
|
||||
struct alignas(CacheLineSize) Accumulator;
|
||||
|
||||
template<IndexType TransformedFeatureDimensions>
|
||||
class FeatureTransformer;
|
||||
|
||||
// Class that holds the result of affine transformation of input features
|
||||
template<IndexType Size>
|
||||
struct alignas(CacheLineSize) Accumulator {
|
||||
std::array<std::array<std::int16_t, Size>, COLOR_NB> accumulation;
|
||||
std::array<std::array<std::int16_t, L1>, COLOR_NB> accumulation;
|
||||
std::array<std::array<std::int32_t, PSQTBuckets>, COLOR_NB> psqtAccumulation;
|
||||
std::array<bool, COLOR_NB> computed = {};
|
||||
};
|
||||
@@ -59,92 +56,50 @@ struct alignas(CacheLineSize) Accumulator {
|
||||
// This idea, was first described by Luecx (author of Koivisto) and
|
||||
// is commonly referred to as "Finny Tables".
|
||||
struct AccumulatorCaches {
|
||||
|
||||
template<typename Networks>
|
||||
AccumulatorCaches(const Networks& networks) {
|
||||
clear(networks);
|
||||
template<typename Network>
|
||||
AccumulatorCaches(const Network& network) {
|
||||
clear(network);
|
||||
}
|
||||
|
||||
template<IndexType Size>
|
||||
struct alignas(CacheLineSize) Cache {
|
||||
struct alignas(CacheLineSize) Entry {
|
||||
std::array<BiasType, L1> accumulation;
|
||||
std::array<PSQTWeightType, PSQTBuckets> psqtAccumulation;
|
||||
std::array<Piece, SQUARE_NB> pieces;
|
||||
Bitboard pieceBB;
|
||||
|
||||
struct alignas(CacheLineSize) Entry {
|
||||
std::array<BiasType, Size> accumulation;
|
||||
std::array<PSQTWeightType, PSQTBuckets> psqtAccumulation;
|
||||
std::array<Piece, SQUARE_NB> pieces;
|
||||
Bitboard pieceBB;
|
||||
|
||||
// To initialize a refresh entry, we set all its bitboards empty,
|
||||
// so we put the biases in the accumulation, without any weights on top
|
||||
void clear(const std::array<BiasType, Size>& biases) {
|
||||
accumulation = biases;
|
||||
std::memset(reinterpret_cast<std::byte*>(this) + offsetof(Entry, psqtAccumulation),
|
||||
0, sizeof(Entry) - offsetof(Entry, psqtAccumulation));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Network>
|
||||
void clear(const Network& network) {
|
||||
for (auto& entries1D : entries)
|
||||
for (auto& entry : entries1D)
|
||||
entry.clear(network.featureTransformer.biases);
|
||||
// To initialize a refresh entry, we set all its bitboards empty,
|
||||
// so we put the biases in the accumulation, without any weights on top
|
||||
void clear(const std::array<BiasType, L1>& biases) {
|
||||
accumulation = biases;
|
||||
std::memset(reinterpret_cast<std::byte*>(this) + offsetof(Entry, psqtAccumulation), 0,
|
||||
sizeof(Entry) - offsetof(Entry, psqtAccumulation));
|
||||
}
|
||||
|
||||
std::array<Entry, COLOR_NB>& operator[](Square sq) { return entries[sq]; }
|
||||
|
||||
std::array<std::array<Entry, COLOR_NB>, SQUARE_NB> entries;
|
||||
};
|
||||
|
||||
template<typename Networks>
|
||||
void clear(const Networks& networks) {
|
||||
big.clear(networks.big);
|
||||
small.clear(networks.small);
|
||||
template<typename Network>
|
||||
void clear(const Network& network) {
|
||||
for (auto& entries1D : entries)
|
||||
for (auto& entry : entries1D)
|
||||
entry.clear(network.featureTransformer.biases);
|
||||
}
|
||||
|
||||
Cache<TransformedFeatureDimensionsBig> big;
|
||||
Cache<TransformedFeatureDimensionsSmall> small;
|
||||
std::array<Entry, COLOR_NB>& operator[](Square sq) { return entries[sq]; }
|
||||
|
||||
std::array<std::array<Entry, COLOR_NB>, SQUARE_NB> entries;
|
||||
};
|
||||
|
||||
|
||||
template<typename FeatureSet>
|
||||
struct AccumulatorState {
|
||||
Accumulator<TransformedFeatureDimensionsBig> accumulatorBig;
|
||||
Accumulator<TransformedFeatureDimensionsSmall> accumulatorSmall;
|
||||
typename FeatureSet::DiffType diff;
|
||||
|
||||
template<IndexType Size>
|
||||
auto& acc() noexcept {
|
||||
static_assert(Size == TransformedFeatureDimensionsBig
|
||||
|| Size == TransformedFeatureDimensionsSmall,
|
||||
"Invalid size for accumulator");
|
||||
|
||||
if constexpr (Size == TransformedFeatureDimensionsBig)
|
||||
return accumulatorBig;
|
||||
else if constexpr (Size == TransformedFeatureDimensionsSmall)
|
||||
return accumulatorSmall;
|
||||
}
|
||||
|
||||
template<IndexType Size>
|
||||
const auto& acc() const noexcept {
|
||||
static_assert(Size == TransformedFeatureDimensionsBig
|
||||
|| Size == TransformedFeatureDimensionsSmall,
|
||||
"Invalid size for accumulator");
|
||||
|
||||
if constexpr (Size == TransformedFeatureDimensionsBig)
|
||||
return accumulatorBig;
|
||||
else if constexpr (Size == TransformedFeatureDimensionsSmall)
|
||||
return accumulatorSmall;
|
||||
}
|
||||
struct AccumulatorState: public Accumulator {
|
||||
typename FeatureSet::DiffType diff;
|
||||
|
||||
void reset(const typename FeatureSet::DiffType& dp) noexcept {
|
||||
diff = dp;
|
||||
accumulatorBig.computed.fill(false);
|
||||
accumulatorSmall.computed.fill(false);
|
||||
computed.fill(false);
|
||||
}
|
||||
|
||||
typename FeatureSet::DiffType& reset() noexcept {
|
||||
accumulatorBig.computed.fill(false);
|
||||
accumulatorSmall.computed.fill(false);
|
||||
computed.fill(false);
|
||||
return diff;
|
||||
}
|
||||
};
|
||||
@@ -160,10 +115,10 @@ class AccumulatorStack {
|
||||
std::pair<DirtyPiece&, DirtyThreats&> push() noexcept;
|
||||
void pop() noexcept;
|
||||
|
||||
template<IndexType Dimensions>
|
||||
void evaluate(const Position& pos,
|
||||
const FeatureTransformer<Dimensions>& featureTransformer,
|
||||
AccumulatorCaches::Cache<Dimensions>& cache) noexcept;
|
||||
void evaluate(const Position& pos,
|
||||
const FeatureTransformer& featureTransformer,
|
||||
// Silence spurious warning on GCC 10
|
||||
[[maybe_unused]] AccumulatorCaches& cache) noexcept;
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
@@ -175,26 +130,27 @@ class AccumulatorStack {
|
||||
template<typename T>
|
||||
[[nodiscard]] std::array<AccumulatorState<T>, MaxSize>& mut_accumulators() noexcept;
|
||||
|
||||
template<typename FeatureSet, IndexType Dimensions>
|
||||
void evaluate_side(Color perspective,
|
||||
const Position& pos,
|
||||
const FeatureTransformer<Dimensions>& featureTransformer,
|
||||
AccumulatorCaches::Cache<Dimensions>& cache) noexcept;
|
||||
template<typename FeatureSet>
|
||||
void evaluate_side(Color perspective,
|
||||
const Position& pos,
|
||||
const FeatureTransformer& featureTransformer,
|
||||
// Silence spurious warning on GCC 10
|
||||
[[maybe_unused]] AccumulatorCaches& cache) noexcept;
|
||||
|
||||
template<typename FeatureSet, IndexType Dimensions>
|
||||
template<typename FeatureSet>
|
||||
[[nodiscard]] std::size_t find_last_usable_accumulator(Color perspective) const noexcept;
|
||||
|
||||
template<typename FeatureSet, IndexType Dimensions>
|
||||
void forward_update_incremental(Color perspective,
|
||||
const Position& pos,
|
||||
const FeatureTransformer<Dimensions>& featureTransformer,
|
||||
const std::size_t begin) noexcept;
|
||||
template<typename FeatureSet>
|
||||
void forward_update_incremental(Color perspective,
|
||||
const Position& pos,
|
||||
const FeatureTransformer& featureTransformer,
|
||||
const std::size_t begin) noexcept;
|
||||
|
||||
template<typename FeatureSet, IndexType Dimensions>
|
||||
void backward_update_incremental(Color perspective,
|
||||
const Position& pos,
|
||||
const FeatureTransformer<Dimensions>& featureTransformer,
|
||||
const std::size_t end) noexcept;
|
||||
template<typename FeatureSet>
|
||||
void backward_update_incremental(Color perspective,
|
||||
const Position& pos,
|
||||
const FeatureTransformer& featureTransformer,
|
||||
const std::size_t end) noexcept;
|
||||
|
||||
std::array<AccumulatorState<PSQFeatureSet>, MaxSize> psq_accumulators;
|
||||
std::array<AccumulatorState<ThreatFeatureSet>, MaxSize> threat_accumulators;
|
||||
|
||||
@@ -40,13 +40,9 @@ using ThreatFeatureSet = Features::FullThreats;
|
||||
using PSQFeatureSet = Features::HalfKAv2_hm;
|
||||
|
||||
// Number of input feature dimensions after conversion
|
||||
constexpr IndexType TransformedFeatureDimensionsBig = 1024;
|
||||
constexpr int L2Big = 31;
|
||||
constexpr int L3Big = 32;
|
||||
|
||||
constexpr IndexType TransformedFeatureDimensionsSmall = 128;
|
||||
constexpr int L2Small = 15;
|
||||
constexpr int L3Small = 32;
|
||||
constexpr IndexType L1 = 1024;
|
||||
constexpr int L2 = 31;
|
||||
constexpr int L3 = 32;
|
||||
|
||||
constexpr IndexType PSQTBuckets = 8;
|
||||
constexpr IndexType LayerStacks = 8;
|
||||
@@ -57,7 +53,6 @@ constexpr IndexType LayerStacks = 8;
|
||||
static_assert(PSQTBuckets % 8 == 0,
|
||||
"Per feature PSQT values cannot be processed at granularity lower than 8 at a time.");
|
||||
|
||||
template<IndexType L1, int L2, int L3>
|
||||
struct NetworkArchitecture {
|
||||
static constexpr IndexType TransformedFeatureDimensions = L1;
|
||||
static constexpr int FC_0_OUTPUTS = L2;
|
||||
@@ -147,10 +142,9 @@ struct NetworkArchitecture {
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE
|
||||
|
||||
template<Stockfish::Eval::NNUE::IndexType L1, int L2, int L3>
|
||||
struct std::hash<Stockfish::Eval::NNUE::NetworkArchitecture<L1, L2, L3>> {
|
||||
std::size_t
|
||||
operator()(const Stockfish::Eval::NNUE::NetworkArchitecture<L1, L2, L3>& arch) const noexcept {
|
||||
template<>
|
||||
struct std::hash<Stockfish::Eval::NNUE::NetworkArchitecture> {
|
||||
std::size_t operator()(const Stockfish::Eval::NNUE::NetworkArchitecture& arch) const noexcept {
|
||||
return arch.get_content_hash();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -78,22 +78,17 @@ void permute(std::array<T, N>& data, const std::array<std::size_t, OrderSize>& o
|
||||
}
|
||||
|
||||
// Input feature converter
|
||||
template<IndexType TransformedFeatureDimensions>
|
||||
class FeatureTransformer {
|
||||
static constexpr bool UseThreats =
|
||||
(TransformedFeatureDimensions == TransformedFeatureDimensionsBig);
|
||||
// Number of output dimensions for one side
|
||||
static constexpr IndexType HalfDimensions = TransformedFeatureDimensions;
|
||||
static constexpr IndexType HalfDimensions = L1;
|
||||
|
||||
public:
|
||||
// Output type
|
||||
using OutputType = TransformedFeatureType;
|
||||
|
||||
// Number of input/output dimensions
|
||||
static constexpr IndexType InputDimensions = PSQFeatureSet::Dimensions;
|
||||
static constexpr IndexType ThreatInputDimensions = ThreatFeatureSet::Dimensions;
|
||||
static constexpr IndexType TotalInputDimensions =
|
||||
InputDimensions + (UseThreats ? ThreatInputDimensions : 0);
|
||||
static constexpr IndexType InputDimensions =
|
||||
PSQFeatureSet::Dimensions + ThreatFeatureSet::Dimensions;
|
||||
static constexpr IndexType OutputDimensions = HalfDimensions;
|
||||
|
||||
// Size of forward propagation buffer
|
||||
@@ -134,8 +129,7 @@ class FeatureTransformer {
|
||||
|
||||
// Hash value embedded in the evaluation file
|
||||
static constexpr std::uint32_t get_hash_value() {
|
||||
return (UseThreats ? combine_hash({ThreatFeatureSet::HashValue, PSQFeatureSet::HashValue})
|
||||
: PSQFeatureSet::HashValue)
|
||||
return combine_hash({ThreatFeatureSet::HashValue, PSQFeatureSet::HashValue})
|
||||
^ (OutputDimensions * 2);
|
||||
}
|
||||
|
||||
@@ -143,35 +137,23 @@ class FeatureTransformer {
|
||||
permute<16>(biases, PackusEpi16Order);
|
||||
permute<16>(weights, PackusEpi16Order);
|
||||
|
||||
if constexpr (UseThreats)
|
||||
permute<8>(threatWeights, PackusEpi16Order);
|
||||
permute<8>(threatWeights, PackusEpi16Order);
|
||||
}
|
||||
|
||||
void unpermute_weights() {
|
||||
permute<16>(biases, InversePackusEpi16Order);
|
||||
permute<16>(weights, InversePackusEpi16Order);
|
||||
|
||||
if constexpr (UseThreats)
|
||||
permute<8>(threatWeights, InversePackusEpi16Order);
|
||||
permute<8>(threatWeights, InversePackusEpi16Order);
|
||||
}
|
||||
|
||||
// Read network parameters
|
||||
bool read_parameters(std::istream& stream) {
|
||||
read_leb_128(stream, biases);
|
||||
|
||||
if constexpr (UseThreats)
|
||||
{
|
||||
read_little_endian<ThreatWeightType>(stream, threatWeights.data(),
|
||||
ThreatInputDimensions * HalfDimensions);
|
||||
read_leb_128(stream, weights);
|
||||
|
||||
read_leb_128(stream, threatPsqtWeights, psqtWeights);
|
||||
}
|
||||
else
|
||||
{
|
||||
read_leb_128(stream, weights);
|
||||
read_leb_128(stream, psqtWeights);
|
||||
}
|
||||
read_little_endian<ThreatWeightType>(stream, threatWeights.data(),
|
||||
ThreatFeatureSet::Dimensions * HalfDimensions);
|
||||
read_leb_128(stream, weights);
|
||||
read_leb_128(stream, threatPsqtWeights, psqtWeights);
|
||||
|
||||
permute_weights();
|
||||
|
||||
@@ -186,30 +168,22 @@ class FeatureTransformer {
|
||||
|
||||
write_leb_128<BiasType>(stream, copy->biases);
|
||||
|
||||
if constexpr (UseThreats)
|
||||
{
|
||||
write_little_endian<ThreatWeightType>(stream, copy->threatWeights.data(),
|
||||
ThreatInputDimensions * HalfDimensions);
|
||||
write_leb_128<WeightType>(stream, copy->weights);
|
||||
write_little_endian<ThreatWeightType>(stream, copy->threatWeights.data(),
|
||||
ThreatFeatureSet::Dimensions * HalfDimensions);
|
||||
write_leb_128<WeightType>(stream, copy->weights);
|
||||
|
||||
auto combinedPsqtWeights =
|
||||
std::make_unique<std::array<PSQTWeightType, TotalInputDimensions * PSQTBuckets>>();
|
||||
auto combinedPsqtWeights =
|
||||
std::make_unique<std::array<PSQTWeightType, InputDimensions * PSQTBuckets>>();
|
||||
|
||||
std::copy(std::begin(copy->threatPsqtWeights),
|
||||
std::begin(copy->threatPsqtWeights) + ThreatInputDimensions * PSQTBuckets,
|
||||
combinedPsqtWeights->begin());
|
||||
std::copy(std::begin(copy->threatPsqtWeights),
|
||||
std::begin(copy->threatPsqtWeights) + ThreatFeatureSet::Dimensions * PSQTBuckets,
|
||||
combinedPsqtWeights->begin());
|
||||
|
||||
std::copy(std::begin(copy->psqtWeights),
|
||||
std::begin(copy->psqtWeights) + InputDimensions * PSQTBuckets,
|
||||
combinedPsqtWeights->begin() + ThreatInputDimensions * PSQTBuckets);
|
||||
std::copy(std::begin(copy->psqtWeights),
|
||||
std::begin(copy->psqtWeights) + PSQFeatureSet::Dimensions * PSQTBuckets,
|
||||
combinedPsqtWeights->begin() + ThreatFeatureSet::Dimensions * PSQTBuckets);
|
||||
|
||||
write_leb_128<PSQTWeightType>(stream, *combinedPsqtWeights);
|
||||
}
|
||||
else
|
||||
{
|
||||
write_leb_128<WeightType>(stream, copy->weights);
|
||||
write_leb_128<PSQTWeightType>(stream, copy->psqtWeights);
|
||||
}
|
||||
write_leb_128<PSQTWeightType>(stream, *combinedPsqtWeights);
|
||||
|
||||
return !stream.fail();
|
||||
}
|
||||
@@ -221,11 +195,8 @@ class FeatureTransformer {
|
||||
hash_combine(h, get_raw_data_hash(weights));
|
||||
hash_combine(h, get_raw_data_hash(psqtWeights));
|
||||
|
||||
if constexpr (UseThreats)
|
||||
{
|
||||
hash_combine(h, get_raw_data_hash(threatWeights));
|
||||
hash_combine(h, get_raw_data_hash(threatPsqtWeights));
|
||||
}
|
||||
hash_combine(h, get_raw_data_hash(threatWeights));
|
||||
hash_combine(h, get_raw_data_hash(threatPsqtWeights));
|
||||
|
||||
hash_combine(h, get_hash_value());
|
||||
|
||||
@@ -233,11 +204,11 @@ class FeatureTransformer {
|
||||
}
|
||||
|
||||
// Convert input features
|
||||
std::int32_t transform(const Position& pos,
|
||||
AccumulatorStack& accumulatorStack,
|
||||
AccumulatorCaches::Cache<HalfDimensions>& cache,
|
||||
OutputType* output,
|
||||
int bucket) const {
|
||||
std::int32_t transform(const Position& pos,
|
||||
AccumulatorStack& accumulatorStack,
|
||||
AccumulatorCaches& cache,
|
||||
OutputType* output,
|
||||
int bucket) const {
|
||||
|
||||
using namespace SIMD;
|
||||
accumulatorStack.evaluate(pos, *this, cache);
|
||||
@@ -245,24 +216,17 @@ class FeatureTransformer {
|
||||
const auto& threatAccumulatorState = accumulatorStack.latest<ThreatFeatureSet>();
|
||||
|
||||
const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()};
|
||||
const auto& psqtAccumulation = (accumulatorState.acc<HalfDimensions>()).psqtAccumulation;
|
||||
const auto& psqtAccumulation = accumulatorState.psqtAccumulation;
|
||||
auto psqt =
|
||||
(psqtAccumulation[perspectives[0]][bucket] - psqtAccumulation[perspectives[1]][bucket]);
|
||||
|
||||
if constexpr (UseThreats)
|
||||
{
|
||||
const auto& threatPsqtAccumulation =
|
||||
(threatAccumulatorState.acc<HalfDimensions>()).psqtAccumulation;
|
||||
psqt = (psqt + threatPsqtAccumulation[perspectives[0]][bucket]
|
||||
- threatPsqtAccumulation[perspectives[1]][bucket])
|
||||
/ 2;
|
||||
}
|
||||
else
|
||||
psqt /= 2;
|
||||
const auto& threatPsqtAccumulation = threatAccumulatorState.psqtAccumulation;
|
||||
psqt = (psqt + threatPsqtAccumulation[perspectives[0]][bucket]
|
||||
- threatPsqtAccumulation[perspectives[1]][bucket])
|
||||
/ 2;
|
||||
|
||||
const auto& accumulation = (accumulatorState.acc<HalfDimensions>()).accumulation;
|
||||
const auto& threatAccumulation =
|
||||
(threatAccumulatorState.acc<HalfDimensions>()).accumulation;
|
||||
const auto& accumulation = accumulatorState.accumulation;
|
||||
const auto& threatAccumulation = threatAccumulatorState.accumulation;
|
||||
|
||||
for (IndexType p = 0; p < 2; ++p)
|
||||
{
|
||||
@@ -341,48 +305,27 @@ class FeatureTransformer {
|
||||
#else
|
||||
6;
|
||||
#endif
|
||||
if constexpr (UseThreats)
|
||||
|
||||
const vec_t* tin0 =
|
||||
reinterpret_cast<const vec_t*>(&(threatAccumulation[perspectives[p]][0]));
|
||||
const vec_t* tin1 = reinterpret_cast<const vec_t*>(
|
||||
&(threatAccumulation[perspectives[p]][HalfDimensions / 2]));
|
||||
for (IndexType j = 0; j < NumOutputChunks; ++j)
|
||||
{
|
||||
const vec_t* tin0 =
|
||||
reinterpret_cast<const vec_t*>(&(threatAccumulation[perspectives[p]][0]));
|
||||
const vec_t* tin1 = reinterpret_cast<const vec_t*>(
|
||||
&(threatAccumulation[perspectives[p]][HalfDimensions / 2]));
|
||||
for (IndexType j = 0; j < NumOutputChunks; ++j)
|
||||
{
|
||||
const vec_t acc0a = vec_add_16(in0[j * 2 + 0], tin0[j * 2 + 0]);
|
||||
const vec_t acc0b = vec_add_16(in0[j * 2 + 1], tin0[j * 2 + 1]);
|
||||
const vec_t acc1a = vec_add_16(in1[j * 2 + 0], tin1[j * 2 + 0]);
|
||||
const vec_t acc1b = vec_add_16(in1[j * 2 + 1], tin1[j * 2 + 1]);
|
||||
const vec_t acc0a = vec_add_16(in0[j * 2 + 0], tin0[j * 2 + 0]);
|
||||
const vec_t acc0b = vec_add_16(in0[j * 2 + 1], tin0[j * 2 + 1]);
|
||||
const vec_t acc1a = vec_add_16(in1[j * 2 + 0], tin1[j * 2 + 0]);
|
||||
const vec_t acc1b = vec_add_16(in1[j * 2 + 1], tin1[j * 2 + 1]);
|
||||
|
||||
const vec_t sum0a =
|
||||
vec_slli_16(vec_max_16(vec_min_16(acc0a, One), Zero), shift);
|
||||
const vec_t sum0b =
|
||||
vec_slli_16(vec_max_16(vec_min_16(acc0b, One), Zero), shift);
|
||||
const vec_t sum1a = vec_min_16(acc1a, One);
|
||||
const vec_t sum1b = vec_min_16(acc1b, One);
|
||||
const vec_t sum0a = vec_slli_16(vec_max_16(vec_min_16(acc0a, One), Zero), shift);
|
||||
const vec_t sum0b = vec_slli_16(vec_max_16(vec_min_16(acc0b, One), Zero), shift);
|
||||
const vec_t sum1a = vec_min_16(acc1a, One);
|
||||
const vec_t sum1b = vec_min_16(acc1b, One);
|
||||
|
||||
const vec_t pa = vec_mulhi_16(sum0a, sum1a);
|
||||
const vec_t pb = vec_mulhi_16(sum0b, sum1b);
|
||||
const vec_t pa = vec_mulhi_16(sum0a, sum1a);
|
||||
const vec_t pb = vec_mulhi_16(sum0b, sum1b);
|
||||
|
||||
out[j] = vec_packus_16(pa, pb);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (IndexType j = 0; j < NumOutputChunks; ++j)
|
||||
{
|
||||
const vec_t sum0a =
|
||||
vec_slli_16(vec_max_16(vec_min_16(in0[j * 2 + 0], One), Zero), shift);
|
||||
const vec_t sum0b =
|
||||
vec_slli_16(vec_max_16(vec_min_16(in0[j * 2 + 1], One), Zero), shift);
|
||||
const vec_t sum1a = vec_min_16(in1[j * 2 + 0], One);
|
||||
const vec_t sum1b = vec_min_16(in1[j * 2 + 1], One);
|
||||
|
||||
const vec_t pa = vec_mulhi_16(sum0a, sum1a);
|
||||
const vec_t pb = vec_mulhi_16(sum0b, sum1b);
|
||||
|
||||
out[j] = vec_packus_16(pa, pb);
|
||||
}
|
||||
out[j] = vec_packus_16(pa, pb);
|
||||
}
|
||||
|
||||
#else
|
||||
@@ -393,12 +336,9 @@ class FeatureTransformer {
|
||||
BiasType sum1 =
|
||||
accumulation[static_cast<int>(perspectives[p])][j + HalfDimensions / 2];
|
||||
|
||||
if constexpr (UseThreats)
|
||||
{
|
||||
sum0 += threatAccumulation[static_cast<int>(perspectives[p])][j + 0];
|
||||
sum1 +=
|
||||
threatAccumulation[static_cast<int>(perspectives[p])][j + HalfDimensions / 2];
|
||||
}
|
||||
sum0 += threatAccumulation[static_cast<int>(perspectives[p])][j + 0];
|
||||
sum1 +=
|
||||
threatAccumulation[static_cast<int>(perspectives[p])][j + HalfDimensions / 2];
|
||||
|
||||
sum0 = std::clamp<BiasType>(sum0, 0, 255);
|
||||
sum1 = std::clamp<BiasType>(sum1, 0, 255);
|
||||
@@ -413,24 +353,21 @@ class FeatureTransformer {
|
||||
} // end of function transform()
|
||||
|
||||
alignas(CacheLineSize) std::array<BiasType, HalfDimensions> biases;
|
||||
alignas(CacheLineSize) std::array<WeightType, HalfDimensions * InputDimensions> weights;
|
||||
alignas(
|
||||
CacheLineSize) std::array<WeightType, HalfDimensions * PSQFeatureSet::Dimensions> weights;
|
||||
alignas(CacheLineSize)
|
||||
std::array<ThreatWeightType,
|
||||
UseThreats ? HalfDimensions * ThreatInputDimensions : 0> threatWeights;
|
||||
alignas(CacheLineSize) std::array<PSQTWeightType, InputDimensions * PSQTBuckets> psqtWeights;
|
||||
std::array<ThreatWeightType, HalfDimensions * ThreatFeatureSet::Dimensions> threatWeights;
|
||||
alignas(CacheLineSize)
|
||||
std::array<PSQTWeightType,
|
||||
UseThreats ? ThreatInputDimensions * PSQTBuckets : 0> threatPsqtWeights;
|
||||
std::array<PSQTWeightType, PSQFeatureSet::Dimensions * PSQTBuckets> psqtWeights;
|
||||
alignas(CacheLineSize)
|
||||
std::array<PSQTWeightType, ThreatFeatureSet::Dimensions * PSQTBuckets> threatPsqtWeights;
|
||||
};
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE
|
||||
|
||||
|
||||
template<Stockfish::Eval::NNUE::IndexType TransformedFeatureDimensions>
|
||||
struct std::hash<Stockfish::Eval::NNUE::FeatureTransformer<TransformedFeatureDimensions>> {
|
||||
std::size_t
|
||||
operator()(const Stockfish::Eval::NNUE::FeatureTransformer<TransformedFeatureDimensions>& ft)
|
||||
const noexcept {
|
||||
template<>
|
||||
struct std::hash<Stockfish::Eval::NNUE::FeatureTransformer> {
|
||||
std::size_t operator()(const Stockfish::Eval::NNUE::FeatureTransformer& ft) const noexcept {
|
||||
return ft.get_content_hash();
|
||||
}
|
||||
};
|
||||
|
||||
+8
-37
@@ -55,16 +55,16 @@ void format_cp_aligned_dot(Value v, std::stringstream& stream, const Position& p
|
||||
// Returns a string with the value of each piece on a board,
|
||||
// and a table for (PSQT, Layers) values bucket by bucket.
|
||||
std::string
|
||||
trace(Position& pos, const Eval::NNUE::Networks& networks, Eval::NNUE::AccumulatorCaches& caches) {
|
||||
trace(Position& pos, const Eval::NNUE::Network& network, Eval::NNUE::AccumulatorCaches& caches) {
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
auto accumulators = std::make_unique<AccumulatorStack>();
|
||||
|
||||
accumulators->reset();
|
||||
auto tSmall = networks.small.trace_evaluate(pos, *accumulators, caches.small);
|
||||
|
||||
ss << "(Small net) NNUE network contributions (Normalized, "
|
||||
auto t = network.trace_evaluate(pos, *accumulators, caches);
|
||||
|
||||
ss << "NNUE network contributions (Normalized, "
|
||||
<< (pos.side_to_move() == WHITE ? "White to move)" : "Black to move)") << std::endl
|
||||
<< "+------------+------------+------------+------------+\n"
|
||||
<< "| Bucket | Material | Positional | Total |\n"
|
||||
@@ -75,45 +75,16 @@ trace(Position& pos, const Eval::NNUE::Networks& networks, Eval::NNUE::Accumulat
|
||||
{
|
||||
ss << "| " << bucket << " " //
|
||||
<< " | ";
|
||||
format_cp_aligned_dot(tSmall.psqt[bucket], ss, pos);
|
||||
format_cp_aligned_dot(t.psqt[bucket], ss, pos);
|
||||
ss << " " //
|
||||
<< " | ";
|
||||
format_cp_aligned_dot(tSmall.positional[bucket], ss, pos);
|
||||
format_cp_aligned_dot(t.positional[bucket], ss, pos);
|
||||
ss << " " //
|
||||
<< " | ";
|
||||
format_cp_aligned_dot(tSmall.psqt[bucket] + tSmall.positional[bucket], ss, pos);
|
||||
format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], ss, pos);
|
||||
ss << " " //
|
||||
<< " |";
|
||||
if (bucket == tSmall.correctBucket)
|
||||
ss << " <-- this bucket is used";
|
||||
ss << '\n';
|
||||
}
|
||||
|
||||
ss << "+------------+------------+------------+------------+\n\n";
|
||||
|
||||
auto tBig = networks.big.trace_evaluate(pos, *accumulators, caches.big);
|
||||
|
||||
ss << "(Big net) NNUE network contributions (Normalized, "
|
||||
<< (pos.side_to_move() == WHITE ? "White to move)" : "Black to move)") << std::endl
|
||||
<< "+------------+------------+------------+------------+\n"
|
||||
<< "| Bucket | Material | Positional | Total |\n"
|
||||
<< "| | (PSQT) | (Layers) | |\n"
|
||||
<< "+------------+------------+------------+------------+\n";
|
||||
|
||||
for (std::size_t bucket = 0; bucket < LayerStacks; ++bucket)
|
||||
{
|
||||
ss << "| " << bucket << " " //
|
||||
<< " | ";
|
||||
format_cp_aligned_dot(tBig.psqt[bucket], ss, pos);
|
||||
ss << " " //
|
||||
<< " | ";
|
||||
format_cp_aligned_dot(tBig.positional[bucket], ss, pos);
|
||||
ss << " " //
|
||||
<< " | ";
|
||||
format_cp_aligned_dot(tBig.psqt[bucket] + tBig.positional[bucket], ss, pos);
|
||||
ss << " " //
|
||||
<< " |";
|
||||
if (bucket == tBig.correctBucket)
|
||||
if (bucket == t.correctBucket)
|
||||
ss << " <-- this bucket is used";
|
||||
ss << '\n';
|
||||
}
|
||||
|
||||
@@ -52,10 +52,10 @@ struct NnueEvalTrace {
|
||||
std::size_t correctBucket;
|
||||
};
|
||||
|
||||
struct Networks;
|
||||
class Network;
|
||||
struct AccumulatorCaches;
|
||||
|
||||
std::string trace(Position& pos, const Networks& networks, AccumulatorCaches& caches);
|
||||
std::string trace(Position& pos, const Network& network, AccumulatorCaches& caches);
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE
|
||||
} // namespace Stockfish
|
||||
|
||||
+5
-4
@@ -1195,10 +1195,11 @@ void write_multiple_dirties(const Position& p,
|
||||
#endif
|
||||
|
||||
template<bool ComputeRay>
|
||||
void Position::update_piece_threats(Piece pc,
|
||||
bool putPiece,
|
||||
Square s,
|
||||
DirtyThreats* const dts,
|
||||
void Position::update_piece_threats(Piece pc,
|
||||
bool putPiece,
|
||||
Square s,
|
||||
DirtyThreats* const dts,
|
||||
// Silence spurious warning on GCC 10
|
||||
[[maybe_unused]] Bitboard noRaysContaining) const {
|
||||
const Bitboard occupied = pieces();
|
||||
const Bitboard rookQueens = pieces(ROOK, QUEEN);
|
||||
|
||||
+5
-5
@@ -171,15 +171,15 @@ Search::Worker::Worker(SharedState& sharedState,
|
||||
options(sharedState.options),
|
||||
threads(sharedState.threads),
|
||||
tt(sharedState.tt),
|
||||
networks(sharedState.networks),
|
||||
refreshTable(networks[token]) {
|
||||
network(sharedState.network),
|
||||
refreshTable(network[token]) {
|
||||
clear();
|
||||
}
|
||||
|
||||
void Search::Worker::ensure_network_replicated() {
|
||||
// Access once to force lazy initialization.
|
||||
// We do this because we want to avoid initialization during search.
|
||||
(void) (networks[numaAccessToken]);
|
||||
(void) (network[numaAccessToken]);
|
||||
}
|
||||
|
||||
void Search::Worker::start_searching() {
|
||||
@@ -639,7 +639,7 @@ void Search::Worker::clear() {
|
||||
for (size_t i = 1; i < reductions.size(); ++i)
|
||||
reductions[i] = int(2763 / 128.0 * std::log(i));
|
||||
|
||||
refreshTable.clear(networks[numaAccessToken]);
|
||||
refreshTable.clear(network[numaAccessToken]);
|
||||
}
|
||||
|
||||
|
||||
@@ -1785,7 +1785,7 @@ TimePoint Search::Worker::elapsed() const {
|
||||
TimePoint Search::Worker::elapsed_time() const { return main_manager()->tm.elapsed_time(); }
|
||||
|
||||
Value Search::Worker::evaluate(const Position& pos) {
|
||||
return Eval::evaluate(networks[numaAccessToken], pos, accumulatorStack, refreshTable,
|
||||
return Eval::evaluate(network[numaAccessToken], pos, accumulatorStack, refreshTable,
|
||||
optimism[pos.side_to_move()]);
|
||||
}
|
||||
|
||||
|
||||
+15
-15
@@ -175,22 +175,22 @@ struct LimitsType {
|
||||
// The UCI stores the uci options, thread pool, and transposition table.
|
||||
// This struct is used to easily forward data to the Search::Worker class.
|
||||
struct SharedState {
|
||||
SharedState(const OptionsMap& optionsMap,
|
||||
ThreadPool& threadPool,
|
||||
TranspositionTable& transpositionTable,
|
||||
std::map<NumaIndex, SharedHistories>& sharedHists,
|
||||
const LazyNumaReplicatedSystemWide<Eval::NNUE::Networks>& nets) :
|
||||
SharedState(const OptionsMap& optionsMap,
|
||||
ThreadPool& threadPool,
|
||||
TranspositionTable& transpositionTable,
|
||||
std::map<NumaIndex, SharedHistories>& sharedHists,
|
||||
const LazyNumaReplicatedSystemWide<Eval::NNUE::Network>& net) :
|
||||
options(optionsMap),
|
||||
threads(threadPool),
|
||||
tt(transpositionTable),
|
||||
sharedHistories(sharedHists),
|
||||
networks(nets) {}
|
||||
network(net) {}
|
||||
|
||||
const OptionsMap& options;
|
||||
ThreadPool& threads;
|
||||
TranspositionTable& tt;
|
||||
std::map<NumaIndex, SharedHistories>& sharedHistories;
|
||||
const LazyNumaReplicatedSystemWide<Eval::NNUE::Networks>& networks;
|
||||
const OptionsMap& options;
|
||||
ThreadPool& threads;
|
||||
TranspositionTable& tt;
|
||||
std::map<NumaIndex, SharedHistories>& sharedHistories;
|
||||
const LazyNumaReplicatedSystemWide<Eval::NNUE::Network>& network;
|
||||
};
|
||||
|
||||
class Worker;
|
||||
@@ -396,10 +396,10 @@ class Worker {
|
||||
|
||||
Tablebases::Config tbConfig;
|
||||
|
||||
const OptionsMap& options;
|
||||
ThreadPool& threads;
|
||||
TranspositionTable& tt;
|
||||
const LazyNumaReplicatedSystemWide<Eval::NNUE::Networks>& networks;
|
||||
const OptionsMap& options;
|
||||
ThreadPool& threads;
|
||||
TranspositionTable& tt;
|
||||
const LazyNumaReplicatedSystemWide<Eval::NNUE::Network>& network;
|
||||
|
||||
// Used by NNUE
|
||||
Eval::NNUE::AccumulatorStack accumulatorStack;
|
||||
|
||||
+6
-9
@@ -82,7 +82,7 @@ void UCIEngine::init_search_update_listeners() {
|
||||
engine.set_on_update_full(
|
||||
[this](const auto& i) { on_update_full(i, engine.get_options()["UCI_ShowWDL"]); });
|
||||
engine.set_on_bestmove([](const auto& bm, const auto& p) { on_bestmove(bm, p); });
|
||||
engine.set_on_verify_networks([](const auto& s) { print_info_string(s); });
|
||||
engine.set_on_verify_network([](const auto& s) { print_info_string(s); });
|
||||
}
|
||||
|
||||
void UCIEngine::loop() {
|
||||
@@ -152,15 +152,12 @@ void UCIEngine::loop() {
|
||||
sync_cout << compiler_info() << sync_endl;
|
||||
else if (token == "export_net")
|
||||
{
|
||||
std::pair<std::optional<std::string>, std::string> files[2];
|
||||
std::pair<std::optional<std::string>, std::string> file;
|
||||
|
||||
if (is >> files[0].second)
|
||||
files[0].first = files[0].second;
|
||||
if (is >> file.second)
|
||||
file.first = file.second;
|
||||
|
||||
if (is >> files[1].second)
|
||||
files[1].first = files[1].second;
|
||||
|
||||
engine.save_network(files);
|
||||
engine.save_network(file);
|
||||
}
|
||||
else if (token == "--help" || token == "help" || token == "--license" || token == "license")
|
||||
sync_cout
|
||||
@@ -309,7 +306,7 @@ void UCIEngine::benchmark(std::istream& args) {
|
||||
engine.set_on_iter([](const auto&) {});
|
||||
engine.set_on_update_no_moves([](const auto&) {});
|
||||
engine.set_on_bestmove([](const auto&, const auto&) {});
|
||||
engine.set_on_verify_networks([](const auto&) {});
|
||||
engine.set_on_verify_network([](const auto&) {});
|
||||
|
||||
Benchmark::BenchmarkSetup setup = Benchmark::setup_benchmark(args);
|
||||
|
||||
|
||||
@@ -2,15 +2,7 @@
|
||||
|
||||
#include "../evaluate.h"
|
||||
|
||||
extern const unsigned char gEmbeddedNNUEBigData[] = {
|
||||
#embed EvalFileDefaultNameBig
|
||||
extern const unsigned char gEmbeddedNNUEData[] = {
|
||||
#embed EvalFileDefaultName
|
||||
};
|
||||
extern const unsigned int gEmbeddedNNUEBigSize = sizeof(gEmbeddedNNUEBigData);
|
||||
extern const unsigned char* const gEmbeddedNNUEBigEnd = gEmbeddedNNUEBigData + gEmbeddedNNUEBigSize;
|
||||
|
||||
extern const unsigned char gEmbeddedNNUESmallData[] = {
|
||||
#embed EvalFileDefaultNameSmall
|
||||
};
|
||||
extern const unsigned int gEmbeddedNNUESmallSize = sizeof(gEmbeddedNNUESmallData);
|
||||
extern const unsigned char* const gEmbeddedNNUESmallEnd =
|
||||
gEmbeddedNNUESmallData + gEmbeddedNNUESmallSize;
|
||||
extern const unsigned int gEmbeddedNNUESize = sizeof(gEmbeddedNNUEData);
|
||||
|
||||
Reference in New Issue
Block a user