Add helpers for managing aligned memory

Previously, we had two type aliases, LargePagePtr and AlignedPtr, which
required manually initializing the aligned memory for the pointer.

The new helpers:

- make_unique_aligned
- make_unique_large_page

are now available for allocating aligned memory (with large pages). They
behave similarly to std::make_unique, ensuring objects allocated with
these functions follow RAII.

The old approach had issues with initializing non-trivial types or
arrays of objects. The evaluation function of the network is now a
unique pointer to an array instead of an array of unique pointers.

Memory related functions have been moved into memory.h

Passed High Hash Pressure Test Non-Regression STC:
https://tests.stockfishchess.org/tests/view/665b2b36586058766677cfd2
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 476992 W: 122426 L: 122677 D: 231889
Ptnml(0-2): 1145, 51027, 134419, 50744, 1161

Failed Normal Non-Regression STC:
https://tests.stockfishchess.org/tests/view/665b2997586058766677cfc8
LLR: -2.94 (-2.94,2.94) <-1.75,0.25>
Total: 877312 W: 225233 L: 226395 D: 425684
Ptnml(0-2): 2110, 94642, 246239, 93630, 2035

Probably a fluke since there shouldn't be a real slowndown and it has also
passed the high hash pressure test.

closes https://github.com/official-stockfish/Stockfish/pull/5332

No functional change
This commit is contained in:
Disservin
2024-06-03 23:11:59 +02:00
parent a2a7edf4c8
commit 00a28ae325
11 changed files with 492 additions and 312 deletions
+27 -50
View File
@@ -20,7 +20,6 @@
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <memory>
@@ -30,6 +29,7 @@
#include "../evaluate.h"
#include "../incbin/incbin.h"
#include "../memory.h"
#include "../misc.h"
#include "../position.h"
#include "../types.h"
@@ -86,23 +86,6 @@ namespace Stockfish::Eval::NNUE {
namespace Detail {
// Initialize the evaluation function parameters
template<typename T>
void initialize(AlignedPtr<T>& pointer) {
pointer.reset(reinterpret_cast<T*>(std_aligned_alloc(alignof(T), sizeof(T))));
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 read_parameters(std::istream& stream, T& reference) {
@@ -128,19 +111,17 @@ template<typename Arch, typename Transformer>
Network<Arch, Transformer>::Network(const Network<Arch, Transformer>& other) :
evalFile(other.evalFile),
embeddedType(other.embeddedType) {
if (other.featureTransformer)
{
Detail::initialize(featureTransformer);
*featureTransformer = *other.featureTransformer;
}
featureTransformer = make_unique_large_page<Transformer>(*other.featureTransformer);
network = make_unique_aligned<Arch[]>(LayerStacks);
if (!other.network)
return;
for (std::size_t i = 0; i < LayerStacks; ++i)
{
if (other.network[i])
{
Detail::initialize(network[i]);
*(network[i]) = *(other.network[i]);
}
}
network[i] = other.network[i];
}
template<typename Arch, typename Transformer>
@@ -150,18 +131,15 @@ Network<Arch, Transformer>::operator=(const Network<Arch, Transformer>& other) {
embeddedType = other.embeddedType;
if (other.featureTransformer)
{
Detail::initialize(featureTransformer);
*featureTransformer = *other.featureTransformer;
}
featureTransformer = make_unique_large_page<Transformer>(*other.featureTransformer);
network = make_unique_aligned<Arch[]>(LayerStacks);
if (!other.network)
return *this;
for (std::size_t i = 0; i < LayerStacks; ++i)
{
if (other.network[i])
{
Detail::initialize(network[i]);
*(network[i]) = *(other.network[i]);
}
}
network[i] = other.network[i];
return *this;
}
@@ -253,7 +231,7 @@ Value Network<Arch, Transformer>::evaluate(const Position&
const int bucket = (pos.count<ALL_PIECES>() - 1) / 4;
const auto psqt = featureTransformer->transform(pos, cache, transformedFeatures, bucket);
const auto positional = network[bucket]->propagate(transformedFeatures);
const auto positional = network[bucket].propagate(transformedFeatures);
if (complexity)
*complexity = std::abs(psqt - positional) / OutputScale;
@@ -292,11 +270,11 @@ void Network<Arch, Transformer>::verify(std::string evalfilePath) const {
exit(EXIT_FAILURE);
}
size_t size = sizeof(*featureTransformer) + sizeof(*network) * LayerStacks;
size_t size = sizeof(*featureTransformer) + sizeof(Arch) * LayerStacks;
sync_cout << "info string NNUE evaluation using " << evalfilePath << " ("
<< size / (1024 * 1024) << "MiB, (" << featureTransformer->InputDimensions << ", "
<< network[0]->TransformedFeatureDimensions << ", " << network[0]->FC_0_OUTPUTS
<< ", " << network[0]->FC_1_OUTPUTS << ", 1))" << sync_endl;
<< network[0].TransformedFeatureDimensions << ", " << network[0].FC_0_OUTPUTS << ", "
<< network[0].FC_1_OUTPUTS << ", 1))" << sync_endl;
}
@@ -333,7 +311,7 @@ Network<Arch, Transformer>::trace_evaluate(const Position&
{
const auto materialist =
featureTransformer->transform(pos, cache, transformedFeatures, bucket);
const auto positional = network[bucket]->propagate(transformedFeatures);
const auto positional = network[bucket].propagate(transformedFeatures);
t.psqt[bucket] = static_cast<Value>(materialist / OutputScale);
t.positional[bucket] = static_cast<Value>(positional / OutputScale);
@@ -386,9 +364,8 @@ void Network<Arch, Transformer>::load_internal() {
template<typename Arch, typename Transformer>
void Network<Arch, Transformer>::initialize() {
Detail::initialize(featureTransformer);
for (std::size_t i = 0; i < LayerStacks; ++i)
Detail::initialize(network[i]);
featureTransformer = make_unique_large_page<Transformer>();
network = make_unique_aligned<Arch[]>(LayerStacks);
}
@@ -455,7 +432,7 @@ bool Network<Arch, Transformer>::read_parameters(std::istream& stream,
return false;
for (std::size_t i = 0; i < LayerStacks; ++i)
{
if (!Detail::read_parameters(stream, *(network[i])))
if (!Detail::read_parameters(stream, network[i]))
return false;
}
return stream && stream.peek() == std::ios::traits_type::eof();
@@ -471,7 +448,7 @@ bool Network<Arch, Transformer>::write_parameters(std::ostream& stream,
return false;
for (std::size_t i = 0; i < LayerStacks; ++i)
{
if (!Detail::write_parameters(stream, *(network[i])))
if (!Detail::write_parameters(stream, network[i]))
return false;
}
return bool(stream);