]> git.sesse.net Git - stockfish/blob - src/nnue/evaluate_nnue.cpp
Merge remote-tracking branch 'upstream/master' into HEAD
[stockfish] / src / nnue / evaluate_nnue.cpp
1 /*
2   Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3   Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
4
5   Stockfish is free software: you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation, either version 3 of the License, or
8   (at your option) any later version.
9
10   Stockfish is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 // Code for calculating NNUE evaluation function
20
21 #include <iostream>
22 #include <set>
23
24 #include "../evaluate.h"
25 #include "../position.h"
26 #include "../misc.h"
27 #include "../uci.h"
28 #include "../types.h"
29
30 #include "evaluate_nnue.h"
31
32 namespace Stockfish::Eval::NNUE {
33
34   // Input feature converter
35   LargePagePtr<FeatureTransformer> feature_transformer;
36
37   // Evaluation function
38   AlignedPtr<Network> network;
39
40   // Evaluation function file name
41   std::string fileName;
42
43   namespace Detail {
44
45   // Initialize the evaluation function parameters
46   template <typename T>
47   void Initialize(AlignedPtr<T>& pointer) {
48
49     pointer.reset(reinterpret_cast<T*>(std_aligned_alloc(alignof(T), sizeof(T))));
50     std::memset(pointer.get(), 0, sizeof(T));
51   }
52
53   template <typename T>
54   void Initialize(LargePagePtr<T>& pointer) {
55
56     static_assert(alignof(T) <= 4096, "aligned_large_pages_alloc() may fail for such a big alignment requirement of T");
57     pointer.reset(reinterpret_cast<T*>(aligned_large_pages_alloc(sizeof(T))));
58     std::memset(pointer.get(), 0, sizeof(T));
59   }
60
61   // Read evaluation function parameters
62   template <typename T>
63   bool ReadParameters(std::istream& stream, T& reference) {
64
65     std::uint32_t header;
66     header = read_little_endian<std::uint32_t>(stream);
67     if (!stream || header != T::GetHashValue()) return false;
68     return reference.ReadParameters(stream);
69   }
70
71   }  // namespace Detail
72
73   // Initialize the evaluation function parameters
74   void Initialize() {
75
76     Detail::Initialize(feature_transformer);
77     Detail::Initialize(network);
78   }
79
80   // Read network header
81   bool ReadHeader(std::istream& stream, std::uint32_t* hash_value, std::string* architecture)
82   {
83     std::uint32_t version, size;
84
85     version     = read_little_endian<std::uint32_t>(stream);
86     *hash_value = read_little_endian<std::uint32_t>(stream);
87     size        = read_little_endian<std::uint32_t>(stream);
88     if (!stream || version != kVersion) return false;
89     architecture->resize(size);
90     stream.read(&(*architecture)[0], size);
91     return !stream.fail();
92   }
93
94   // Read network parameters
95   bool ReadParameters(std::istream& stream) {
96
97     std::uint32_t hash_value;
98     std::string architecture;
99     if (!ReadHeader(stream, &hash_value, &architecture)) return false;
100     if (hash_value != kHashValue) return false;
101     if (!Detail::ReadParameters(stream, *feature_transformer)) return false;
102     if (!Detail::ReadParameters(stream, *network)) return false;
103     return stream && stream.peek() == std::ios::traits_type::eof();
104   }
105
106   // Evaluation function. Perform differential calculation.
107   Value evaluate(const Position& pos) {
108
109     // We manually align the arrays on the stack because with gcc < 9.3
110     // overaligning stack variables with alignas() doesn't work correctly.
111
112     constexpr uint64_t alignment = kCacheLineSize;
113
114 #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN)
115     TransformedFeatureType transformed_features_unaligned[
116       FeatureTransformer::kBufferSize + alignment / sizeof(TransformedFeatureType)];
117     char buffer_unaligned[Network::kBufferSize + alignment];
118
119     auto* transformed_features = align_ptr_up<alignment>(&transformed_features_unaligned[0]);
120     auto* buffer = align_ptr_up<alignment>(&buffer_unaligned[0]);
121 #else
122     alignas(alignment)
123       TransformedFeatureType transformed_features[FeatureTransformer::kBufferSize];
124     alignas(alignment) char buffer[Network::kBufferSize];
125 #endif
126
127     ASSERT_ALIGNED(transformed_features, alignment);
128     ASSERT_ALIGNED(buffer, alignment);
129
130     feature_transformer->Transform(pos, transformed_features);
131     const auto output = network->Propagate(transformed_features, buffer);
132
133     return static_cast<Value>(output[0] / FV_SCALE);
134   }
135
136   // Load eval, from a file stream or a memory stream
137   bool load_eval(std::string name, std::istream& stream) {
138
139     Initialize();
140     fileName = name;
141     return ReadParameters(stream);
142   }
143
144 } // namespace Stockfish::Eval::NNUE