Assume network file is in little-endian byte order

This patch fixes the byte order when reading 16- and 32-bit values from the network file on a big-endian machine.

Bytes are ordered in read_le() using unsigned arithmetic, which doesn't need tricks to determine the endianness of the machine. Unfortunately the compiler doesn't seem to be able to optimise the ordering operation, but reading in the weights is not a time-critical operation and the extra time it takes should not be noticeable.

Big endian systems are still untested with NNUE.

fixes #3007

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

No functional change.
This commit is contained in:
syzygy1
2020-08-15 16:50:39 +02:00
committed by Joost VandeVondele
parent 8cf43c6317
commit 72dc7a5c54
4 changed files with 31 additions and 13 deletions

View File

@@ -77,7 +77,7 @@ namespace Eval::NNUE {
bool ReadParameters(std::istream& stream, const AlignedPtr<T>& pointer) { bool ReadParameters(std::istream& stream, const AlignedPtr<T>& pointer) {
std::uint32_t header; std::uint32_t header;
stream.read(reinterpret_cast<char*>(&header), sizeof(header)); header = read_le<std::uint32_t>(stream);
if (!stream || header != T::GetHashValue()) return false; if (!stream || header != T::GetHashValue()) return false;
return pointer->ReadParameters(stream); return pointer->ReadParameters(stream);
} }
@@ -96,9 +96,9 @@ namespace Eval::NNUE {
std::uint32_t* hash_value, std::string* architecture) { std::uint32_t* hash_value, std::string* architecture) {
std::uint32_t version, size; std::uint32_t version, size;
stream.read(reinterpret_cast<char*>(&version), sizeof(version)); version = read_le<std::uint32_t>(stream);
stream.read(reinterpret_cast<char*>(hash_value), sizeof(*hash_value)); *hash_value = read_le<std::uint32_t>(stream);
stream.read(reinterpret_cast<char*>(&size), sizeof(size)); size = read_le<std::uint32_t>(stream);
if (!stream || version != kVersion) return false; if (!stream || version != kVersion) return false;
architecture->resize(size); architecture->resize(size);
stream.read(&(*architecture)[0], size); stream.read(&(*architecture)[0], size);

View File

@@ -62,11 +62,10 @@ namespace Eval::NNUE::Layers {
// Read network parameters // Read network parameters
bool ReadParameters(std::istream& stream) { bool ReadParameters(std::istream& stream) {
if (!previous_layer_.ReadParameters(stream)) return false; if (!previous_layer_.ReadParameters(stream)) return false;
stream.read(reinterpret_cast<char*>(biases_), for (std::size_t i = 0; i < kOutputDimensions; ++i)
kOutputDimensions * sizeof(BiasType)); biases_[i] = read_le<BiasType>(stream);
stream.read(reinterpret_cast<char*>(weights_), for (std::size_t i = 0; i < kOutputDimensions * kPaddedInputDimensions; ++i)
kOutputDimensions * kPaddedInputDimensions * weights_[i] = read_le<WeightType>(stream);
sizeof(WeightType));
return !stream.fail(); return !stream.fail();
} }

View File

@@ -21,6 +21,9 @@
#ifndef NNUE_COMMON_H_INCLUDED #ifndef NNUE_COMMON_H_INCLUDED
#define NNUE_COMMON_H_INCLUDED #define NNUE_COMMON_H_INCLUDED
#include <cstring>
#include <iostream>
#if defined(USE_AVX2) #if defined(USE_AVX2)
#include <immintrin.h> #include <immintrin.h>
@@ -101,6 +104,22 @@ namespace Eval::NNUE {
return (n + base - 1) / base * base; return (n + base - 1) / base * base;
} }
// Read a signed or unsigned integer from a stream in little-endian order
template <typename IntType>
inline IntType read_le(std::istream& stream) {
// Read the relevant bytes from the stream in little-endian order
std::uint8_t u[sizeof(IntType)];
stream.read(reinterpret_cast<char*>(u), sizeof(IntType));
// Use unsigned arithmetic to convert to machine order
typename std::make_unsigned<IntType>::type v = 0;
for (std::size_t i = 0; i < sizeof(IntType); ++i)
v = (v << 8) | u[sizeof(IntType) - i - 1];
// Copy the machine-ordered bytes into a potentially signed value
IntType w;
std::memcpy(&w, &v, sizeof(IntType));
return w;
}
} // namespace Eval::NNUE } // namespace Eval::NNUE
#endif // #ifndef NNUE_COMMON_H_INCLUDED #endif // #ifndef NNUE_COMMON_H_INCLUDED

View File

@@ -55,10 +55,10 @@ namespace Eval::NNUE {
// Read network parameters // Read network parameters
bool ReadParameters(std::istream& stream) { bool ReadParameters(std::istream& stream) {
stream.read(reinterpret_cast<char*>(biases_), for (std::size_t i = 0; i < kHalfDimensions; ++i)
kHalfDimensions * sizeof(BiasType)); biases_[i] = read_le<BiasType>(stream);
stream.read(reinterpret_cast<char*>(weights_), for (std::size_t i = 0; i < kHalfDimensions * kInputDimensions; ++i)
kHalfDimensions * kInputDimensions * sizeof(WeightType)); weights_[i] = read_le<WeightType>(stream);
return !stream.fail(); return !stream.fail();
} }