Merge remote-tracking branch 'remotes/origin/master' into trainer

This commit is contained in:
noobpwnftw
2020-09-23 18:43:54 +08:00
19 changed files with 304 additions and 107 deletions
+26 -18
View File
@@ -56,7 +56,7 @@ namespace Eval {
return UseNNUEMode::False;
}
void init_NNUE() {
void NNUE::init() {
useNNUE = nnue_mode_from_option(Options["Use NNUE"]);
if (useNNUE == UseNNUEMode::False)
@@ -81,8 +81,8 @@ namespace Eval {
}
}
/// verify_NNUE() verifies that the last net used was loaded successfully
void verify_NNUE() {
/// NNUE::verify() verifies that the last net used was loaded successfully
void NNUE::verify() {
string eval_file = string(Options["EvalFile"]);
@@ -984,24 +984,32 @@ make_v:
/// evaluation of the position from the point of view of the side to move.
Value Eval::evaluate(const Position& pos) {
if (useNNUE == UseNNUEMode::Pure) {
return NNUE::evaluate(pos);
}
// Use classical eval if there is a large imbalance
// If there is a moderate imbalance, use classical eval with probability (1/8),
// as derived from the node counter.
bool useClassical = abs(eg_value(pos.psq_score())) * 16 > NNUEThreshold1 * (16 + pos.rule50_count());
bool classical = (useNNUE == UseNNUEMode::False)
|| useClassical
|| (abs(eg_value(pos.psq_score())) > PawnValueMg / 4 && !(pos.this_thread()->nodes & 0xB));
Value v = classical ? Evaluation<NO_TRACE>(pos).value()
: NNUE::evaluate(pos);
Value v;
if ( useClassical
&& useNNUE != UseNNUEMode::False
&& abs(v) * 16 < NNUEThreshold2 * (16 + pos.rule50_count()))
if (Eval::useNNUE == UseNNUEMode::Pure) {
v = NNUE::evaluate(pos);
}
else if (Eval::useNNUE == UseNNUEMode::False)
v = Evaluation<NO_TRACE>(pos).value();
else
{
// scale and shift NNUE for compatibility with search and classical evaluation
auto adjusted_NNUE = [&](){ return NNUE::evaluate(pos) * 5 / 4 + Tempo; };
// if there is PSQ imbalance use classical eval, with small probability if it is small
Value psq = Value(abs(eg_value(pos.psq_score())));
int r50 = 16 + pos.rule50_count();
bool largePsq = psq * 16 > (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50;
bool classical = largePsq || (psq > PawnValueMg / 4 && !(pos.this_thread()->nodes & 0xB));
v = classical ? Evaluation<NO_TRACE>(pos).value() : adjusted_NNUE();
// if the classical eval is small and imbalance large, use NNUE nevertheless.
if ( largePsq
&& abs(v) * 16 < NNUEThreshold2 * r50)
v = adjusted_NNUE();
}
// Damp down the evaluation linearly when shuffling
v = v * (100 - pos.rule50_count()) / 100;
+3 -3
View File
@@ -38,8 +38,6 @@ namespace Eval {
extern UseNNUEMode useNNUE;
extern std::string eval_file_loaded;
void init_NNUE();
void verify_NNUE();
// The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue
// for the build process (profile-build and fishtest) to work. Do not change the
@@ -49,7 +47,9 @@ namespace Eval {
namespace NNUE {
Value evaluate(const Position& pos);
bool load_eval(std::string streamName, std::istream& stream);
bool load_eval(std::string name, std::istream& stream);
void init();
void verify();
} // namespace NNUE
+1 -1
View File
@@ -1168,7 +1168,7 @@ namespace Learner
<< " detect_draw_by_insufficient_mating_material = " << detect_draw_by_insufficient_mating_material << endl;
// Show if the training data generator uses NNUE.
Eval::verify_NNUE();
Eval::NNUE::verify();
Threads.main()->ponder = false;
+4 -4
View File
@@ -1841,7 +1841,7 @@ namespace Learner
if (use_convert_plain)
{
Eval::init_NNUE();
Eval::NNUE::init();
cout << "convert_plain.." << endl;
convert_plain(filenames, output_file_name);
return;
@@ -1849,7 +1849,7 @@ namespace Learner
if (use_convert_bin)
{
Eval::init_NNUE();
Eval::NNUE::init();
cout << "convert_bin.." << endl;
convert_bin(
filenames,
@@ -1870,7 +1870,7 @@ namespace Learner
if (use_convert_bin_from_pgn_extract)
{
Eval::init_NNUE();
Eval::NNUE::init();
cout << "convert_bin_from_pgn-extract.." << endl;
convert_bin_from_pgn_extract(
filenames,
@@ -1938,7 +1938,7 @@ namespace Learner
cout << "init.." << endl;
// Read evaluation function parameters
Eval::init_NNUE();
Eval::NNUE::init();
Threads.main()->ponder = false;
+1 -1
View File
@@ -12,7 +12,7 @@ void MultiThink::go_think()
// Read evaluation function, etc.
// In the case of the learn command, the value of the evaluation function may be corrected after reading the evaluation function, so
// Skip memory corruption check.
Eval::init_NNUE();
Eval::NNUE::init();
// Call the derived class's init().
init();
+1 -1
View File
@@ -45,7 +45,7 @@ int main(int argc, char* argv[]) {
Endgames::init();
Threads.set(size_t(Options["Threads"]));
Search::clear(); // After threads are up
Eval::init_NNUE();
Eval::NNUE::init();
UCI::loop(argc, argv);
+25 -32
View File
@@ -357,27 +357,11 @@ void std_aligned_free(void* ptr) {
#endif
}
/// aligned_ttmem_alloc() will return suitably aligned memory, if possible using large pages.
/// The returned pointer is the aligned one, while the mem argument is the one that needs
/// to be passed to free. With c++17 some of this functionality could be simplified.
/// aligned_large_pages_alloc() will return suitably aligned memory, if possible using large pages.
#if defined(__linux__) && !defined(__ANDROID__)
#if defined(_WIN32)
void* aligned_ttmem_alloc(size_t allocSize, void*& mem) {
constexpr size_t alignment = 2 * 1024 * 1024; // assumed 2MB page sizes
size_t size = ((allocSize + alignment - 1) / alignment) * alignment; // multiple of alignment
if (posix_memalign(&mem, alignment, size))
mem = nullptr;
#if defined(MADV_HUGEPAGE)
madvise(mem, allocSize, MADV_HUGEPAGE);
#endif
return mem;
}
#elif defined(_WIN64)
static void* aligned_ttmem_alloc_large_pages(size_t allocSize) {
static void* aligned_large_pages_alloc_win(size_t allocSize) {
HANDLE hProcessToken { };
LUID luid { };
@@ -422,12 +406,13 @@ static void* aligned_ttmem_alloc_large_pages(size_t allocSize) {
return mem;
}
void* aligned_ttmem_alloc(size_t allocSize, void*& mem) {
void* aligned_large_pages_alloc(size_t allocSize) {
static bool firstCall = true;
void* mem;
// Try to allocate large pages
mem = aligned_ttmem_alloc_large_pages(allocSize);
mem = aligned_large_pages_alloc_win(allocSize);
// Suppress info strings on the first call. The first call occurs before 'uci'
// is received and in that case this output confuses some GUIs.
@@ -449,23 +434,31 @@ void* aligned_ttmem_alloc(size_t allocSize, void*& mem) {
#else
void* aligned_ttmem_alloc(size_t allocSize, void*& mem) {
void* aligned_large_pages_alloc(size_t allocSize) {
constexpr size_t alignment = 64; // assumed cache line size
size_t size = allocSize + alignment - 1; // allocate some extra space
mem = malloc(size);
void* ret = reinterpret_cast<void*>((uintptr_t(mem) + alignment - 1) & ~uintptr_t(alignment - 1));
return ret;
#if defined(__linux__)
constexpr size_t alignment = 2 * 1024 * 1024; // assumed 2MB page size
#else
constexpr size_t alignment = 4096; // assumed small page size
#endif
// round up to multiples of alignment
size_t size = ((allocSize + alignment - 1) / alignment) * alignment;
void *mem = std_aligned_alloc(alignment, size);
#if defined(MADV_HUGEPAGE)
madvise(mem, size, MADV_HUGEPAGE);
#endif
return mem;
}
#endif
/// aligned_ttmem_free() will free the previously allocated ttmem
/// aligned_large_pages_free() will free the previously allocated ttmem
#if defined(_WIN64)
#if defined(_WIN32)
void aligned_ttmem_free(void* mem) {
void aligned_large_pages_free(void* mem) {
if (mem && !VirtualFree(mem, 0, MEM_RELEASE))
{
@@ -478,8 +471,8 @@ void aligned_ttmem_free(void* mem) {
#else
void aligned_ttmem_free(void *mem) {
free(mem);
void aligned_large_pages_free(void *mem) {
std_aligned_free(mem);
}
#endif
+2 -2
View File
@@ -39,8 +39,8 @@ void prefetch(void* addr);
void start_logger(const std::string& fname);
void* std_aligned_alloc(size_t alignment, size_t size);
void std_aligned_free(void* ptr);
void* aligned_ttmem_alloc(size_t size, void*& mem);
void aligned_ttmem_free(void* mem); // nop if mem == nullptr
void* aligned_large_pages_alloc(size_t size); // memory aligned by page size, min alignment: 4096 bytes
void aligned_large_pages_free(void* mem); // nop if mem == nullptr
void dbg_hit_on(bool b);
void dbg_hit_on(bool c, bool b);
+23 -8
View File
@@ -30,7 +30,7 @@
namespace Eval::NNUE {
uint32_t kpp_board_index[PIECE_NB][COLOR_NB] = {
const uint32_t kpp_board_index[PIECE_NB][COLOR_NB] = {
// convention: W - us, B - them
// viewed from other side, W and B are reversed
{ PS_NONE, PS_NONE },
@@ -52,7 +52,7 @@ namespace Eval::NNUE {
};
// Input feature converter
AlignedPtr<FeatureTransformer> feature_transformer;
LargePagePtr<FeatureTransformer> feature_transformer;
// Evaluation function
AlignedPtr<Network> network;
@@ -79,14 +79,22 @@ namespace Eval::NNUE {
std::memset(pointer.get(), 0, sizeof(T));
}
template <typename T>
void Initialize(LargePagePtr<T>& pointer) {
static_assert(alignof(T) <= 4096, "aligned_large_pages_alloc() may fail for such a big alignment requirement of T");
pointer.reset(reinterpret_cast<T*>(aligned_large_pages_alloc(sizeof(T))));
std::memset(pointer.get(), 0, sizeof(T));
}
// Read evaluation function parameters
template <typename T>
bool ReadParameters(std::istream& stream, const AlignedPtr<T>& pointer) {
bool ReadParameters(std::istream& stream, T& reference) {
std::uint32_t header;
header = read_little_endian<std::uint32_t>(stream);
if (!stream || header != T::GetHashValue()) return false;
return pointer->ReadParameters(stream);
return reference.ReadParameters(stream);
}
// write evaluation function parameters
@@ -97,6 +105,13 @@ namespace Eval::NNUE {
return pointer->WriteParameters(stream);
}
template <typename T>
bool WriteParameters(std::ostream& stream, const LargePagePtr<T>& pointer) {
constexpr std::uint32_t header = T::GetHashValue();
stream.write(reinterpret_cast<const char*>(&header), sizeof(header));
return pointer->WriteParameters(stream);
}
} // namespace Detail
// Initialize the evaluation function parameters
@@ -138,8 +153,8 @@ namespace Eval::NNUE {
std::string architecture;
if (!ReadHeader(stream, &hash_value, &architecture)) return false;
if (hash_value != kHashValue) return false;
if (!Detail::ReadParameters(stream, feature_transformer)) return false;
if (!Detail::ReadParameters(stream, network)) return false;
if (!Detail::ReadParameters(stream, *feature_transformer)) return false;
if (!Detail::ReadParameters(stream, *network)) return false;
return stream && stream.peek() == std::ios::traits_type::eof();
}
// write evaluation function parameters
@@ -162,7 +177,7 @@ namespace Eval::NNUE {
}
// Load eval, from a file stream or a memory stream
bool load_eval(std::string streamName, std::istream& stream) {
bool load_eval(std::string name, std::istream& stream) {
Initialize();
@@ -171,7 +186,7 @@ namespace Eval::NNUE {
std::cout << "info string SkipLoadingEval set to true, Net not loaded!" << std::endl;
return true;
}
fileName = streamName;
fileName = name;
return ReadParameters(stream);
}
+12 -1
View File
@@ -40,11 +40,22 @@ namespace Eval::NNUE {
}
};
template <typename T>
struct LargePageDeleter {
void operator()(T* ptr) const {
ptr->~T();
aligned_large_pages_free(ptr);
}
};
template <typename T>
using AlignedPtr = std::unique_ptr<T, AlignedDeleter<T>>;
template <typename T>
using LargePagePtr = std::unique_ptr<T, LargePageDeleter<T>>;
// Input feature converter
extern AlignedPtr<FeatureTransformer> feature_transformer;
extern LargePagePtr<FeatureTransformer> feature_transformer;
// Evaluation function
extern AlignedPtr<Network> network;
+1 -1
View File
@@ -113,7 +113,7 @@ namespace Eval::NNUE {
PS_END2 = 12 * SQUARE_NB + 1
};
extern uint32_t kpp_board_index[PIECE_NB][COLOR_NB];
extern const uint32_t kpp_board_index[PIECE_NB][COLOR_NB];
// Type of input feature after conversion
using TransformedFeatureType = std::uint8_t;
+10 -12
View File
@@ -217,7 +217,7 @@ void MainThread::search() {
Time.init(Limits, us, rootPos.game_ply());
TT.new_search();
Eval::verify_NNUE();
Eval::NNUE::verify();
if (rootMoves.empty())
{
@@ -454,10 +454,7 @@ void Thread::search() {
++failedHighCnt;
}
else
{
++rootMoves[pvIdx].bestMoveCount;
break;
}
delta += delta / 4 + 5;
@@ -1146,7 +1143,7 @@ moves_loop: // When in check, search starts from here
// Step 16. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be
// re-searched at full depth.
if ( depth >= 3
&& moveCount > 1 + 2 * rootNode + 2 * (PvNode && abs(bestValue) < 2)
&& moveCount > 1 + 2 * rootNode
&& ( !captureOrPromotion
|| moveCountPruning
|| ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha
@@ -1213,14 +1210,14 @@ moves_loop: // When in check, search starts from here
}
else
{
// Increase reduction for captures/promotions if late move and at low depth
if (depth < 8 && moveCount > 2)
r++;
// Increase reduction for captures/promotions if late move and at low depth
if (depth < 8 && moveCount > 2)
r++;
// Unless giving check, this capture is likely bad
if ( !givesCheck
&& ss->staticEval + PieceValue[EG][pos.captured_piece()] + 213 * depth <= alpha)
r++;
// Unless giving check, this capture is likely bad
if ( !givesCheck
&& ss->staticEval + PieceValue[EG][pos.captured_piece()] + 213 * depth <= alpha)
r++;
}
Depth d = std::clamp(newDepth - r, 1, newDepth);
@@ -1567,6 +1564,7 @@ moves_loop: // When in check, search starts from here
[pos.moved_piece(move)]
[to_sq(move)];
// CounterMove based pruning
if ( !captureOrPromotion
&& Search::prune_at_shallow_depth
&& moveCount
-1
View File
@@ -73,7 +73,6 @@ struct RootMove {
Value previousScore = -VALUE_INFINITE;
int selDepth = 0;
int tbRank = 0;
int bestMoveCount = 0;
Value tbScore;
std::vector<Move> pv;
};
+10 -10
View File
@@ -236,16 +236,16 @@ Thread* ThreadPool::get_best_thread() const {
votes[th->rootMoves[0].pv[0]] +=
(th->rootMoves[0].score - minScore + 14) * int(th->completedDepth);
if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY)
{
// Make sure we pick the shortest mate / TB conversion or stave off mate the longest
if (th->rootMoves[0].score > bestThread->rootMoves[0].score)
bestThread = th;
}
else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY
|| ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY
&& votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]]))
bestThread = th;
if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY)
{
// Make sure we pick the shortest mate / TB conversion or stave off mate the longest
if (th->rootMoves[0].score > bestThread->rootMoves[0].score)
bestThread = th;
}
else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY
|| ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY
&& votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]]))
bestThread = th;
}
return bestThread;
+4 -3
View File
@@ -67,11 +67,12 @@ void TranspositionTable::resize(size_t mbSize) {
Threads.main()->wait_for_search_finished();
aligned_ttmem_free(mem);
aligned_large_pages_free(table);
clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster);
table = static_cast<Cluster*>(aligned_ttmem_alloc(clusterCount * sizeof(Cluster), mem));
if (!mem)
table = static_cast<Cluster*>(aligned_large_pages_alloc(clusterCount * sizeof(Cluster)));
if (!table)
{
std::cerr << "Failed to allocate " << mbSize
<< "MB for transposition table." << std::endl;
+1 -2
View File
@@ -73,7 +73,7 @@ class TranspositionTable {
static_assert(sizeof(Cluster) == 32, "Unexpected Cluster size");
public:
~TranspositionTable() { aligned_ttmem_free(mem); }
~TranspositionTable() { aligned_large_pages_free(table); }
void new_search() { generation8 += 8; } // Lower 3 bits are used by PV flag and Bound
TTEntry* probe(const Key key, bool& found) const;
int hashfull() const;
@@ -91,7 +91,6 @@ private:
size_t clusterCount;
Cluster* table;
void* mem;
uint8_t generation8; // Size must be not bigger than TTEntry::genBound8
};
+2 -2
View File
@@ -47,7 +47,7 @@ const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
void test_cmd(Position& pos, istringstream& is)
{
// Initialize as it may be searched.
Eval::init_NNUE();
Eval::NNUE::init();
std::string param;
is >> param;
@@ -100,7 +100,7 @@ namespace {
Position p;
p.set(pos.fen(), Options["UCI_Chess960"], &states->back(), Threads.main());
Eval::verify_NNUE();
Eval::NNUE::verify();
sync_cout << "\n" << Eval::trace(p) << sync_endl;
}
+2 -2
View File
@@ -41,8 +41,8 @@ void on_hash_size(const Option& o) { TT.resize(size_t(o)); }
void on_logger(const Option& o) { start_logger(o); }
void on_threads(const Option& o) { Threads.set(size_t(o)); }
void on_tb_path(const Option& o) { Tablebases::init(o); }
void on_use_NNUE(const Option& ) { Eval::init_NNUE(); }
void on_eval_file(const Option& ) { Eval::init_NNUE(); }
void on_use_NNUE(const Option& ) { Eval::NNUE::init(); }
void on_eval_file(const Option& ) { Eval::NNUE::init(); }
void on_prune_at_shallow_depth(const Option& o) {
Search::prune_at_shallow_depth = o;
}