diff --git a/scripts/net.sh b/scripts/net.sh index 79a0e0088..d14e2be46 100755 --- a/scripts/net.sh +++ b/scripts/net.sh @@ -74,5 +74,5 @@ fetch_network() { return 1 } -fetch_network EvalFileDefaultNameBig && - fetch_network EvalFileDefaultNameSmall +fetch_network EvalFileDefaultName + diff --git a/src/engine.cpp b/src/engine.cpp index 786e88e21..361d8457d 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -62,7 +62,7 @@ Engine::Engine(std::optional path) : numaContext(NumaConfig::from_system(DefaultNumaPolicy)), states(new std::deque(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 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 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&& f) { - onVerifyNetworks = std::move(f); +void Engine::set_on_verify_network(std::function&& 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 Engine::get_default_networks() const { +std::unique_ptr Engine::get_default_network() const { - auto networks_ = - std::make_unique(NN::EvalFile{EvalFileDefaultNameBig, "None", ""}, - NN::EvalFile{EvalFileDefaultNameSmall, "None", ""}); + auto network_ = std::make_unique(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::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::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; } diff --git a/src/engine.h b/src/engine.h index e22466991..f5d09d4e1 100644 --- a/src/engine.h +++ b/src/engine.h @@ -83,15 +83,14 @@ class Engine { void set_on_update_full(std::function&&); void set_on_iter(std::function&&); void set_on_bestmove(std::function&&); - void set_on_verify_networks(std::function&&); + void set_on_verify_network(std::function&&); // network related - void verify_networks() const; - std::unique_ptr 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::string> files[2]); + void verify_network() const; + std::unique_ptr get_default_network() const; + void load_network(const std::string& file); + void save_network(std::pair, std::string> file); // utility functions @@ -119,13 +118,13 @@ class Engine { Position pos; StateListPtr states; - OptionsMap options; - ThreadPool threads; - TranspositionTable tt; - LazyNumaReplicatedSystemWide networks; + OptionsMap options; + ThreadPool threads; + TranspositionTable tt; + LazyNumaReplicatedSystemWide network; Search::SearchManager::UpdateContext updateContext; - std::function onVerifyNetworks; + std::function onVerifyNetwork; std::map sharedHists; }; diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d93282326..dbd35d720 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #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(); - auto caches = std::make_unique(networks); + auto caches = std::make_unique(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"; diff --git a/src/evaluate.h b/src/evaluate.h index fc82a2465..2a93e70bc 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -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, diff --git a/src/nnue/network.cpp b/src/nnue/network.cpp index b77d87178..b63789e1e 100644 --- a/src/nnue/network.cpp +++ b/src/nnue/network.cpp @@ -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 -void Network::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 dirs = {"", "", rootDirectory, stringify(DEFAULT_NNUE_DIRECTORY)}; @@ -146,8 +114,7 @@ void Network::load(const std::string& rootDirectory, std::str } -template -bool Network::save(const std::optional& filename) const { +bool Network::save(const std::optional& filename) const { std::string actualFilename; std::string msg; @@ -177,16 +144,13 @@ bool Network::save(const std::optional& filename } -template -NetworkOutput -Network::evaluate(const Position& pos, - AccumulatorStack& accumulatorStack, - AccumulatorCaches::Cache& cache) const { +NetworkOutput Network::evaluate(const Position& pos, + AccumulatorStack& accumulatorStack, + AccumulatorCaches& cache) const { constexpr uint64_t alignment = CacheLineSize; - alignas(alignment) - TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize]; + alignas(alignment) TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize]; ASSERT_ALIGNED(transformedFeatures, alignment); @@ -198,9 +162,8 @@ Network::evaluate(const Position& pos } -template -void Network::verify(std::string evalfilePath, - const std::function& f) const { +void Network::verify(std::string evalfilePath, + const std::function& f) const { if (evalfilePath.empty()) evalfilePath = evalFile.defaultName; @@ -229,9 +192,9 @@ void Network::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::verify(std::string } -template -NnueEvalTrace -Network::trace_evaluate(const Position& pos, - AccumulatorStack& accumulatorStack, - AccumulatorCaches::Cache& cache) const { +NnueEvalTrace Network::trace_evaluate(const Position& pos, + AccumulatorStack& accumulatorStack, + AccumulatorCaches& cache) const { constexpr uint64_t alignment = CacheLineSize; - alignas(alignment) - TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize]; + alignas(alignment) TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize]; ASSERT_ALIGNED(transformedFeatures, alignment); @@ -268,9 +228,7 @@ Network::trace_evaluate(const Position& } -template -void Network::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::load_user_net(const std::string& dir, } -template -void Network::load_internal() { +void Network::load_internal() { // C++ way to prepare a buffer for a memory stream class MemoryBuffer: public std::basic_streambuf { public: @@ -293,10 +250,8 @@ void Network::load_internal() { } }; - const auto embedded = get_embedded(embeddedType); - - MemoryBuffer buffer(const_cast(reinterpret_cast(embedded.data)), - size_t(embedded.size)); + MemoryBuffer buffer(const_cast(reinterpret_cast(gEmbeddedNNUEData)), + size_t(gEmbeddedNNUESize)); std::istream stream(&buffer); auto description = load(stream); @@ -309,16 +264,12 @@ void Network::load_internal() { } -template -void Network::initialize() { - initialized = true; -} +void Network::initialize() { initialized = true; } -template -bool Network::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::save(std::ostream& stream, } -template -std::optional Network::load(std::istream& stream) { +std::optional Network::load(std::istream& stream) { initialize(); std::string description; @@ -335,8 +285,7 @@ std::optional Network::load(std::istream& stream } -template -std::size_t Network::get_content_hash() const { +std::size_t Network::get_content_hash() const { if (!initialized) return 0; @@ -345,15 +294,11 @@ std::size_t Network::get_content_hash() const { for (auto&& layerstack : network) hash_combine(h, layerstack); hash_combine(h, evalFile); - hash_combine(h, static_cast(embeddedType)); return h; } // Read network header -template -bool Network::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(stream); @@ -368,10 +313,9 @@ bool Network::read_header(std::istream& stream, // Write network header -template -bool Network::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(stream, Version); write_little_endian(stream, hashValue); write_little_endian(stream, std::uint32_t(desc.size())); @@ -380,9 +324,7 @@ bool Network::write_header(std::ostream& stream, } -template -bool Network::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::read_parameters(std::istream& stream, } -template -bool Network::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::write_parameters(std::ostream& stream, return bool(stream); } -// Explicit template instantiations - -template class Network, - FeatureTransformer>; - -template class Network, - FeatureTransformer>; - } // namespace Stockfish::Eval::NNUE diff --git a/src/nnue/network.h b/src/nnue/network.h index cb433718d..5d7ba066a 100644 --- a/src/nnue/network.h +++ b/src/nnue/network.h @@ -29,11 +29,8 @@ #include #include -#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; // 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 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& cache) const; + NetworkOutput evaluate(const Position& pos, + AccumulatorStack& accumulatorStack, + AccumulatorCaches& cache) const; void verify(std::string evalfilePath, const std::function&) const; - NnueEvalTrace trace_evaluate(const Position& pos, - AccumulatorStack& accumulatorStack, - AccumulatorCaches::Cache& 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 - friend struct AccumulatorCaches::Cache; -}; - -// Definitions of the network types -using SmallFeatureTransformer = FeatureTransformer; -using SmallNetworkArchitecture = - NetworkArchitecture; - -using BigFeatureTransformer = FeatureTransformer; -using BigNetworkArchitecture = NetworkArchitecture; - -using NetworkBig = Network; -using NetworkSmall = Network; - - -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 -struct std::hash> { - std::size_t operator()( - const Stockfish::Eval::NNUE::Network& network) const noexcept { +template<> +struct std::hash { + std::size_t operator()(const Stockfish::Eval::NNUE::Network& network) const noexcept { return network.get_content_hash(); } }; -template<> -struct std::hash { - 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 diff --git a/src/nnue/nnue_accumulator.cpp b/src/nnue/nnue_accumulator.cpp index c1ac1722b..dc6813416 100644 --- a/src/nnue/nnue_accumulator.cpp +++ b/src/nnue/nnue_accumulator.cpp @@ -38,26 +38,23 @@ using namespace SIMD; namespace { -template -void update_accumulator_incremental( - Color perspective, - const FeatureTransformer& featureTransformer, - const Square ksq, - AccumulatorState& target_state, - const AccumulatorState& computed); +template +void update_accumulator_incremental(Color perspective, + const FeatureTransformer& featureTransformer, + const Square ksq, + AccumulatorState& target_state, + const AccumulatorState& computed); -template -void update_accumulator_refresh_cache(Color perspective, - const FeatureTransformer& featureTransformer, - const Position& pos, - AccumulatorState& accumulatorState, - AccumulatorCaches::Cache& cache); +void update_accumulator_refresh_cache(Color perspective, + const FeatureTransformer& featureTransformer, + const Position& pos, + AccumulatorState& accumulatorState, + AccumulatorCaches& cache); -template -void update_threats_accumulator_full(Color perspective, - const FeatureTransformer& featureTransformer, - const Position& pos, - AccumulatorState& accumulatorState); +void update_threats_accumulator_full(Color perspective, + const FeatureTransformer& featureTransformer, + const Position& pos, + AccumulatorState& accumulatorState); } template @@ -120,33 +117,26 @@ void AccumulatorStack::pop() noexcept { size--; } -template -void AccumulatorStack::evaluate(const Position& pos, - const FeatureTransformer& featureTransformer, - AccumulatorCaches::Cache& 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(WHITE, pos, featureTransformer, cache); evaluate_side(BLACK, pos, featureTransformer, cache); - if (UseThreats) - { - evaluate_side(WHITE, pos, featureTransformer, cache); - evaluate_side(BLACK, pos, featureTransformer, cache); - } + evaluate_side(WHITE, pos, featureTransformer, cache); + evaluate_side(BLACK, pos, featureTransformer, cache); } -template -void AccumulatorStack::evaluate_side(Color perspective, - const Position& pos, - const FeatureTransformer& featureTransformer, - AccumulatorCaches::Cache& cache) noexcept { +template +void AccumulatorStack::evaluate_side(Color perspective, + const Position& pos, + const FeatureTransformer& featureTransformer, + AccumulatorCaches& cache) noexcept { - const auto last_usable_accum = - find_last_usable_accumulator(perspective); + const auto last_usable_accum = find_last_usable_accumulator(perspective); - if ((accumulators()[last_usable_accum].template acc()) - .computed[perspective]) + if (accumulators()[last_usable_accum].computed[perspective]) forward_update_incremental(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 +template 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()[curr_idx].template acc()).computed[perspective]) + if (accumulators()[curr_idx].computed[perspective]) return curr_idx; if (FeatureSet::requires_refresh(accumulators()[curr_idx].diff, perspective)) @@ -181,15 +171,14 @@ std::size_t AccumulatorStack::find_last_usable_accumulator(Color perspective) co return 0; } -template -void AccumulatorStack::forward_update_incremental( - Color perspective, - const Position& pos, - const FeatureTransformer& featureTransformer, - const std::size_t begin) noexcept { +template +void AccumulatorStack::forward_update_incremental(Color perspective, + const Position& pos, + const FeatureTransformer& featureTransformer, + const std::size_t begin) noexcept { assert(begin < accumulators().size()); - assert((accumulators()[begin].template acc()).computed[perspective]); + assert(accumulators()[begin].computed[perspective]); const Square ksq = pos.square(perspective); @@ -200,20 +189,19 @@ void AccumulatorStack::forward_update_incremental( accumulators()[next - 1]); } - assert((latest().acc()).computed[perspective]); + assert(latest().computed[perspective]); } -template -void AccumulatorStack::backward_update_incremental( - Color perspective, +template +void AccumulatorStack::backward_update_incremental(Color perspective, - const Position& pos, - const FeatureTransformer& featureTransformer, - const std::size_t end) noexcept { + const Position& pos, + const FeatureTransformer& featureTransformer, + const std::size_t end) noexcept { assert(end < accumulators().size()); assert(end < size); - assert((latest().template acc()).computed[perspective]); + assert(latest().computed[perspective]); const Square ksq = pos.square(perspective); @@ -222,20 +210,9 @@ void AccumulatorStack::backward_update_incremental( mut_accumulators()[next], accumulators()[next + 1]); - assert((accumulators()[end].template acc()).computed[perspective]); + assert(accumulators()[end].computed[perspective]); } -// Explicit template instantiations -template void AccumulatorStack::evaluate( - const Position& pos, - const FeatureTransformer& featureTransformer, - AccumulatorCaches::Cache& cache) noexcept; -template void AccumulatorStack::evaluate( - const Position& pos, - const FeatureTransformer& featureTransformer, - AccumulatorCaches::Cache& cache) noexcept; - - namespace { template(rows)[i]...); } -template +template struct AccumulatorUpdateContext { - Color perspective; - const FeatureTransformer& featureTransformer; - const AccumulatorState& from; - AccumulatorState& to; + Color perspective; + const FeatureTransformer& featureTransformer; + const AccumulatorState& from; + AccumulatorState& to; - AccumulatorUpdateContext(Color persp, - const FeatureTransformer& ft, - const AccumulatorState& accF, - AccumulatorState& accT) noexcept : + AccumulatorUpdateContext(Color persp, + const FeatureTransformer& ft, + const AccumulatorState& accF, + AccumulatorState& accT) noexcept : perspective{persp}, featureTransformer{ft}, from{accF}, @@ -275,6 +252,8 @@ struct AccumulatorUpdateContext { typename... Ts, std::enable_if_t, 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( - (from.template acc()).accumulation[perspective].data(), - (to.template acc()).accumulation[perspective].data(), - to_weight_vector(indices)...); + fused_row_reduce(from.accumulation[perspective].data(), + to.accumulation[perspective].data(), + to_weight_vector(indices)...); fused_row_reduce( - (from.template acc()).psqtAccumulation[perspective].data(), - (to.template acc()).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().accumulation[perspective]; - auto& toAcc = to.template acc().accumulation[perspective]; + constexpr IndexType Dimensions = FeatureTransformer::OutputDimensions; - const auto& fromPsqtAcc = from.template acc().psqtAccumulation[perspective]; - auto& toPsqtAcc = to.template acc().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; + vec_t acc[Tiling::NumRegs]; psqt_vec_t psqt[Tiling::NumPsqtRegs]; @@ -426,25 +406,24 @@ struct AccumulatorUpdateContext { } }; -template -auto make_accumulator_update_context(Color perspective, - const FeatureTransformer& featureTransformer, - const AccumulatorState& accumulatorFrom, - AccumulatorState& accumulatorTo) noexcept { - return AccumulatorUpdateContext{perspective, featureTransformer, - accumulatorFrom, accumulatorTo}; +template +auto make_accumulator_update_context(Color perspective, + const FeatureTransformer& featureTransformer, + const AccumulatorState& accumulatorFrom, + AccumulatorState& accumulatorTo) noexcept { + return AccumulatorUpdateContext{perspective, featureTransformer, accumulatorFrom, + accumulatorTo}; } -template -void update_accumulator_incremental( - Color perspective, - const FeatureTransformer& featureTransformer, - const Square ksq, - AccumulatorState& target_state, - const AccumulatorState& computed) { +template +void update_accumulator_incremental(Color perspective, + const FeatureTransformer& featureTransformer, + const Square ksq, + AccumulatorState& target_state, + const AccumulatorState& computed) { - assert((computed.template acc()).computed[perspective]); - assert(!(target_state.template acc()).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) { const auto* pfBase = &featureTransformer.threatWeights[0]; - auto pfStride = static_cast(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()).computed[perspective] = true; + target_state.computed[perspective] = true; } Bitboard get_changed_pieces(const std::array& oldPieces, @@ -559,12 +538,12 @@ Bitboard get_changed_pieces(const std::array& oldPieces, #endif } -template -void update_accumulator_refresh_cache(Color perspective, - const FeatureTransformer& featureTransformer, - const Position& pos, - AccumulatorState& accumulatorState, - AccumulatorCaches::Cache& cache) { +void update_accumulator_refresh_cache(Color perspective, + const FeatureTransformer& featureTransformer, + const Position& pos, + AccumulatorState& accumulator, + AccumulatorCaches& cache) { + constexpr auto Dimensions = FeatureTransformer::OutputDimensions; using Tiling [[maybe_unused]] = SIMDTiling; @@ -595,7 +574,6 @@ void update_accumulator_refresh_cache(Color pers entry.pieceBB = pos.pieces(); entry.pieces = pos.piece_array(); - auto& accumulator = accumulatorState.acc(); accumulator.computed[perspective] = true; #ifdef VECTOR @@ -705,17 +683,16 @@ void update_accumulator_refresh_cache(Color pers #endif } -template -void update_threats_accumulator_full(Color perspective, - const FeatureTransformer& featureTransformer, - const Position& pos, - AccumulatorState& accumulatorState) { - using Tiling [[maybe_unused]] = SIMDTiling; +void update_threats_accumulator_full(Color perspective, + const FeatureTransformer& featureTransformer, + const Position& pos, + AccumulatorState& accumulator) { + constexpr IndexType Dimensions = FeatureTransformer::OutputDimensions; + using Tiling [[maybe_unused]] = SIMDTiling; ThreatFeatureSet::IndexList active; ThreatFeatureSet::append_active_indices(perspective, pos, active); - auto& accumulator = accumulatorState.acc(); accumulator.computed[perspective] = true; #ifdef VECTOR diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index 438074f43..6673d3c6f 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -37,16 +37,13 @@ class Position; namespace Stockfish::Eval::NNUE { -template struct alignas(CacheLineSize) Accumulator; -template class FeatureTransformer; // Class that holds the result of affine transformation of input features -template struct alignas(CacheLineSize) Accumulator { - std::array, COLOR_NB> accumulation; + std::array, COLOR_NB> accumulation; std::array, COLOR_NB> psqtAccumulation; std::array 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 - AccumulatorCaches(const Networks& networks) { - clear(networks); + template + AccumulatorCaches(const Network& network) { + clear(network); } - template - struct alignas(CacheLineSize) Cache { + struct alignas(CacheLineSize) Entry { + std::array accumulation; + std::array psqtAccumulation; + std::array pieces; + Bitboard pieceBB; - struct alignas(CacheLineSize) Entry { - std::array accumulation; - std::array psqtAccumulation; - std::array 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& biases) { - accumulation = biases; - std::memset(reinterpret_cast(this) + offsetof(Entry, psqtAccumulation), - 0, sizeof(Entry) - offsetof(Entry, psqtAccumulation)); - } - }; - - template - 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& biases) { + accumulation = biases; + std::memset(reinterpret_cast(this) + offsetof(Entry, psqtAccumulation), 0, + sizeof(Entry) - offsetof(Entry, psqtAccumulation)); } - - std::array& operator[](Square sq) { return entries[sq]; } - - std::array, SQUARE_NB> entries; }; - template - void clear(const Networks& networks) { - big.clear(networks.big); - small.clear(networks.small); + template + void clear(const Network& network) { + for (auto& entries1D : entries) + for (auto& entry : entries1D) + entry.clear(network.featureTransformer.biases); } - Cache big; - Cache small; + std::array& operator[](Square sq) { return entries[sq]; } + + std::array, SQUARE_NB> entries; }; template -struct AccumulatorState { - Accumulator accumulatorBig; - Accumulator accumulatorSmall; - typename FeatureSet::DiffType diff; - - template - 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 - 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 push() noexcept; void pop() noexcept; - template - void evaluate(const Position& pos, - const FeatureTransformer& featureTransformer, - AccumulatorCaches::Cache& cache) noexcept; + void evaluate(const Position& pos, + const FeatureTransformer& featureTransformer, + // Silence spurious warning on GCC 10 + [[maybe_unused]] AccumulatorCaches& cache) noexcept; private: template @@ -175,26 +130,27 @@ class AccumulatorStack { template [[nodiscard]] std::array, MaxSize>& mut_accumulators() noexcept; - template - void evaluate_side(Color perspective, - const Position& pos, - const FeatureTransformer& featureTransformer, - AccumulatorCaches::Cache& cache) noexcept; + template + void evaluate_side(Color perspective, + const Position& pos, + const FeatureTransformer& featureTransformer, + // Silence spurious warning on GCC 10 + [[maybe_unused]] AccumulatorCaches& cache) noexcept; - template + template [[nodiscard]] std::size_t find_last_usable_accumulator(Color perspective) const noexcept; - template - void forward_update_incremental(Color perspective, - const Position& pos, - const FeatureTransformer& featureTransformer, - const std::size_t begin) noexcept; + template + void forward_update_incremental(Color perspective, + const Position& pos, + const FeatureTransformer& featureTransformer, + const std::size_t begin) noexcept; - template - void backward_update_incremental(Color perspective, - const Position& pos, - const FeatureTransformer& featureTransformer, - const std::size_t end) noexcept; + template + void backward_update_incremental(Color perspective, + const Position& pos, + const FeatureTransformer& featureTransformer, + const std::size_t end) noexcept; std::array, MaxSize> psq_accumulators; std::array, MaxSize> threat_accumulators; diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index f81dcc0f2..02c38df6d 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -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 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 -struct std::hash> { - std::size_t - operator()(const Stockfish::Eval::NNUE::NetworkArchitecture& arch) const noexcept { +template<> +struct std::hash { + std::size_t operator()(const Stockfish::Eval::NNUE::NetworkArchitecture& arch) const noexcept { return arch.get_content_hash(); } }; diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 798c0fa11..6b913e80f 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -78,22 +78,17 @@ void permute(std::array& data, const std::array& o } // Input feature converter -template 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(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(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(stream, copy->biases); - if constexpr (UseThreats) - { - write_little_endian(stream, copy->threatWeights.data(), - ThreatInputDimensions * HalfDimensions); - write_leb_128(stream, copy->weights); + write_little_endian(stream, copy->threatWeights.data(), + ThreatFeatureSet::Dimensions * HalfDimensions); + write_leb_128(stream, copy->weights); - auto combinedPsqtWeights = - std::make_unique>(); + auto combinedPsqtWeights = + std::make_unique>(); - 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(stream, *combinedPsqtWeights); - } - else - { - write_leb_128(stream, copy->weights); - write_leb_128(stream, copy->psqtWeights); - } + write_leb_128(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& 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(); const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()}; - const auto& psqtAccumulation = (accumulatorState.acc()).psqtAccumulation; + const auto& psqtAccumulation = accumulatorState.psqtAccumulation; auto psqt = (psqtAccumulation[perspectives[0]][bucket] - psqtAccumulation[perspectives[1]][bucket]); - if constexpr (UseThreats) - { - const auto& threatPsqtAccumulation = - (threatAccumulatorState.acc()).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()).accumulation; - const auto& threatAccumulation = - (threatAccumulatorState.acc()).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(&(threatAccumulation[perspectives[p]][0])); + const vec_t* tin1 = reinterpret_cast( + &(threatAccumulation[perspectives[p]][HalfDimensions / 2])); + for (IndexType j = 0; j < NumOutputChunks; ++j) { - const vec_t* tin0 = - reinterpret_cast(&(threatAccumulation[perspectives[p]][0])); - const vec_t* tin1 = reinterpret_cast( - &(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(perspectives[p])][j + HalfDimensions / 2]; - if constexpr (UseThreats) - { - sum0 += threatAccumulation[static_cast(perspectives[p])][j + 0]; - sum1 += - threatAccumulation[static_cast(perspectives[p])][j + HalfDimensions / 2]; - } + sum0 += threatAccumulation[static_cast(perspectives[p])][j + 0]; + sum1 += + threatAccumulation[static_cast(perspectives[p])][j + HalfDimensions / 2]; sum0 = std::clamp(sum0, 0, 255); sum1 = std::clamp(sum1, 0, 255); @@ -413,24 +353,21 @@ class FeatureTransformer { } // end of function transform() alignas(CacheLineSize) std::array biases; - alignas(CacheLineSize) std::array weights; + alignas( + CacheLineSize) std::array weights; alignas(CacheLineSize) - std::array threatWeights; - alignas(CacheLineSize) std::array psqtWeights; + std::array threatWeights; alignas(CacheLineSize) - std::array threatPsqtWeights; + std::array psqtWeights; + alignas(CacheLineSize) + std::array threatPsqtWeights; }; } // namespace Stockfish::Eval::NNUE - -template -struct std::hash> { - std::size_t - operator()(const Stockfish::Eval::NNUE::FeatureTransformer& ft) - const noexcept { +template<> +struct std::hash { + std::size_t operator()(const Stockfish::Eval::NNUE::FeatureTransformer& ft) const noexcept { return ft.get_content_hash(); } }; diff --git a/src/nnue/nnue_misc.cpp b/src/nnue/nnue_misc.cpp index 883a22383..1a02ef7cf 100644 --- a/src/nnue/nnue_misc.cpp +++ b/src/nnue/nnue_misc.cpp @@ -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(); - 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'; } diff --git a/src/nnue/nnue_misc.h b/src/nnue/nnue_misc.h index ecece5589..8b2a1e789 100644 --- a/src/nnue/nnue_misc.h +++ b/src/nnue/nnue_misc.h @@ -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 diff --git a/src/position.cpp b/src/position.cpp index c58c0f03f..71a56cb1b 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1195,10 +1195,11 @@ void write_multiple_dirties(const Position& p, #endif template -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); diff --git a/src/search.cpp b/src/search.cpp index d0f8d55c7..57fc20c73 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -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()]); } diff --git a/src/search.h b/src/search.h index 50aef6aa9..23f6a2b3d 100644 --- a/src/search.h +++ b/src/search.h @@ -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& sharedHists, - const LazyNumaReplicatedSystemWide& nets) : + SharedState(const OptionsMap& optionsMap, + ThreadPool& threadPool, + TranspositionTable& transpositionTable, + std::map& sharedHists, + const LazyNumaReplicatedSystemWide& net) : options(optionsMap), threads(threadPool), tt(transpositionTable), sharedHistories(sharedHists), - networks(nets) {} + network(net) {} - const OptionsMap& options; - ThreadPool& threads; - TranspositionTable& tt; - std::map& sharedHistories; - const LazyNumaReplicatedSystemWide& networks; + const OptionsMap& options; + ThreadPool& threads; + TranspositionTable& tt; + std::map& sharedHistories; + const LazyNumaReplicatedSystemWide& network; }; class Worker; @@ -396,10 +396,10 @@ class Worker { Tablebases::Config tbConfig; - const OptionsMap& options; - ThreadPool& threads; - TranspositionTable& tt; - const LazyNumaReplicatedSystemWide& networks; + const OptionsMap& options; + ThreadPool& threads; + TranspositionTable& tt; + const LazyNumaReplicatedSystemWide& network; // Used by NNUE Eval::NNUE::AccumulatorStack accumulatorStack; diff --git a/src/uci.cpp b/src/uci.cpp index 2f2364736..77d62e916 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -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::string> files[2]; + std::pair, 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); diff --git a/src/universal/nnue_embed.cpp b/src/universal/nnue_embed.cpp index 738cd8ac7..e9d5879f1 100644 --- a/src/universal/nnue_embed.cpp +++ b/src/universal/nnue_embed.cpp @@ -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);