mirror of
https://github.com/official-stockfish/Stockfish.git
synced 2026-07-22 12:47:08 +00:00
Prefetch threat weight rows during append_changed_indices
Passed STC (first run): https://tests.stockfishchess.org/tests/view/698030ee6362aee5c8a552c7 LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 120800 W: 31336 L: 30912 D: 58552 Ptnml(0-2): 330, 13084, 33172, 13460, 354 Passed LTC: https://tests.stockfishchess.org/tests/view/6983946d473df9d1d24a916d LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 86850 W: 22352 L: 21951 D: 42547 Ptnml(0-2): 45, 8506, 25934, 8883, 57 Passed STC SMP (after changes made after opening the PR): https://tests.stockfishchess.org/tests/view/6987b4c1b0f3ca5200aaf9da LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 84480 W: 21994 L: 21627 D: 40859 Ptnml(0-2): 98, 9378, 22932, 9723, 109 Passed STC (second run, as a sanity check made after changes made after opening the PR -- these changed, however, didn't change assembly code produced by the compiler): https://tests.stockfishchess.org/tests/view/6987ad21b0f3ca5200aaf9c9 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 132192 W: 34526 L: 34083 D: 63583 Ptnml(0-2): 421, 14511, 35759, 15014, 391 GCC maps __builtin_prefetch locality=1 to PREFETCHT2 instruction, targeting L2 cache. To make things clear, added template-based approach as suggested by Disservin. PREFETCHT2 should be the appropriate choice for the 78 MB threat weight table: avoids L1d pollution, parks data in L2 where it is promoted to L1d on actual load. With 200-500 cycle lead time from make_index, data has time to arrive in L2. suggestions by Disservin and anematode included closes https://github.com/official-stockfish/Stockfish/pull/6602 No functional change
This commit is contained in:
committed by
Joost VandeVondele
parent
99221e5ee6
commit
238ef05bb0
@@ -471,23 +471,6 @@ uint64_t hash_bytes(const char* data, size_t size) {
|
||||
void start_logger(const std::string& fname) { Logger::start(fname); }
|
||||
|
||||
|
||||
#ifdef NO_PREFETCH
|
||||
|
||||
void prefetch(const void*) {}
|
||||
|
||||
#else
|
||||
|
||||
void prefetch(const void* addr) {
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
_mm_prefetch((char const*) addr, _MM_HINT_T0);
|
||||
#else
|
||||
__builtin_prefetch(addr);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#define GETCWD _getcwd
|
||||
|
||||
+63
-2
@@ -37,6 +37,10 @@
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#if !defined(NO_PREFETCH) && (defined(_MSC_VER) || defined(__INTEL_COMPILER))
|
||||
#include <immintrin.h>
|
||||
#endif
|
||||
|
||||
#define stringify2(x) #x
|
||||
#define stringify(x) stringify2(x)
|
||||
|
||||
@@ -46,10 +50,67 @@ std::string engine_version_info();
|
||||
std::string engine_info(bool to_uci = false);
|
||||
std::string compiler_info();
|
||||
|
||||
// Preloads the given address in L1/L2 cache. This is a non-blocking
|
||||
// Prefetch hint enums for explicit call-site control.
|
||||
enum class PrefetchRw {
|
||||
READ,
|
||||
WRITE
|
||||
};
|
||||
|
||||
// NOTE: PrefetchLoc controls locality / cache level, not whether a prefetch
|
||||
// is issued. In particular, PrefetchLoc::NONE maps to a non-temporal /
|
||||
// lowest-locality prefetch (Intel: _MM_HINT_NTA, GCC/Clang: locality = 0)
|
||||
// and therefore still performs a prefetch. To completely disable
|
||||
// prefetching, define NO_PREFETCH so that prefetch() becomes a no-op.
|
||||
enum class PrefetchLoc {
|
||||
NONE, // Non-temporal / no cache locality (still issues a prefetch)
|
||||
LOW, // Low locality (e.g. T2 / L2)
|
||||
MODERATE, // Moderate locality (e.g. T1 / L1)
|
||||
HIGH // High locality (e.g. T0 / closest cache)
|
||||
};
|
||||
|
||||
// Preloads the given address into cache. This is a non-blocking
|
||||
// function that doesn't stall the CPU waiting for data to be loaded from memory,
|
||||
// which can be quite slow.
|
||||
void prefetch(const void* addr);
|
||||
#ifdef NO_PREFETCH
|
||||
template<PrefetchRw RW = PrefetchRw::READ, PrefetchLoc LOC = PrefetchLoc::HIGH>
|
||||
void prefetch(const void*) {}
|
||||
#elif defined(_MSC_VER) || defined(__INTEL_COMPILER)
|
||||
|
||||
constexpr int get_intel_hint(PrefetchRw rw, PrefetchLoc loc) {
|
||||
if (rw == PrefetchRw::WRITE)
|
||||
{
|
||||
#ifdef _MM_HINT_ET0
|
||||
return _MM_HINT_ET0;
|
||||
#else
|
||||
// Fallback when write-prefetch hint is not available: use T0
|
||||
return _MM_HINT_T0;
|
||||
#endif
|
||||
}
|
||||
switch (loc)
|
||||
{
|
||||
case PrefetchLoc::NONE :
|
||||
return _MM_HINT_NTA;
|
||||
case PrefetchLoc::LOW :
|
||||
return _MM_HINT_T2;
|
||||
case PrefetchLoc::MODERATE :
|
||||
return _MM_HINT_T1;
|
||||
case PrefetchLoc::HIGH :
|
||||
return _MM_HINT_T0;
|
||||
default :
|
||||
return _MM_HINT_T0;
|
||||
}
|
||||
}
|
||||
|
||||
template<PrefetchRw RW = PrefetchRw::READ, PrefetchLoc LOC = PrefetchLoc::HIGH>
|
||||
void prefetch(const void* addr) {
|
||||
_mm_prefetch(static_cast<const char*>(addr), get_intel_hint(RW, LOC));
|
||||
}
|
||||
#else
|
||||
template<PrefetchRw RW = PrefetchRw::READ, PrefetchLoc LOC = PrefetchLoc::HIGH>
|
||||
void prefetch(const void* addr) {
|
||||
__builtin_prefetch(addr, static_cast<int>(RW), static_cast<int>(LOC));
|
||||
}
|
||||
#endif
|
||||
|
||||
void start_logger(const std::string& fname);
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "full_threats.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <utility>
|
||||
@@ -273,13 +274,15 @@ void FullThreats::append_active_indices(Color perspective, const Position& pos,
|
||||
|
||||
// Get a list of indices for recently changed features
|
||||
|
||||
void FullThreats::append_changed_indices(Color perspective,
|
||||
Square ksq,
|
||||
const DiffType& diff,
|
||||
IndexList& removed,
|
||||
IndexList& added,
|
||||
FusedUpdateData* fusedData,
|
||||
bool first) {
|
||||
void FullThreats::append_changed_indices(Color perspective,
|
||||
Square ksq,
|
||||
const DiffType& diff,
|
||||
IndexList& removed,
|
||||
IndexList& added,
|
||||
FusedUpdateData* fusedData,
|
||||
bool first,
|
||||
const ThreatWeightType* prefetchBase,
|
||||
IndexType prefetchStride) {
|
||||
|
||||
for (const auto& dirty : diff.list)
|
||||
{
|
||||
@@ -324,7 +327,12 @@ void FullThreats::append_changed_indices(Color perspective,
|
||||
const IndexType index = make_index(perspective, attacker, from, to, attacked, ksq);
|
||||
|
||||
if (index < Dimensions)
|
||||
{
|
||||
if (prefetchBase)
|
||||
prefetch<PrefetchRw::READ, PrefetchLoc::LOW>(
|
||||
prefetchBase + static_cast<std::ptrdiff_t>(index) * prefetchStride);
|
||||
insert.push_back(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -86,13 +86,15 @@ class FullThreats {
|
||||
static void append_active_indices(Color perspective, const Position& pos, IndexList& active);
|
||||
|
||||
// Get a list of indices for recently changed features
|
||||
static void append_changed_indices(Color perspective,
|
||||
Square ksq,
|
||||
const DiffType& diff,
|
||||
IndexList& removed,
|
||||
IndexList& added,
|
||||
FusedUpdateData* fd = nullptr,
|
||||
bool first = false);
|
||||
static void append_changed_indices(Color perspective,
|
||||
Square ksq,
|
||||
const DiffType& diff,
|
||||
IndexList& removed,
|
||||
IndexList& added,
|
||||
FusedUpdateData* fd = nullptr,
|
||||
bool first = false,
|
||||
const ThreatWeightType* prefetchBase = nullptr,
|
||||
IndexType prefetchStride = 0);
|
||||
|
||||
// Returns whether the change stored in this DirtyPiece means
|
||||
// that a full accumulator refresh is required.
|
||||
|
||||
@@ -554,10 +554,12 @@ void double_inc_update(Color p
|
||||
fusedData.dp2removed = dp2.remove_sq;
|
||||
|
||||
ThreatFeatureSet::IndexList removed, added;
|
||||
const auto* pfBase = &featureTransformer.threatWeights[0];
|
||||
auto pfStride = static_cast<IndexType>(TransformedFeatureDimensions);
|
||||
ThreatFeatureSet::append_changed_indices(perspective, ksq, middle_state.diff, removed, added,
|
||||
&fusedData, true);
|
||||
&fusedData, true, pfBase, pfStride);
|
||||
ThreatFeatureSet::append_changed_indices(perspective, ksq, target_state.diff, removed, added,
|
||||
&fusedData, false);
|
||||
&fusedData, false, pfBase, pfStride);
|
||||
|
||||
auto updateContext =
|
||||
make_accumulator_update_context(perspective, featureTransformer, computed, target_state);
|
||||
@@ -585,10 +587,24 @@ void update_accumulator_incremental(
|
||||
// In this case, the maximum size of both feature addition and removal
|
||||
// is 2, since we are incrementally updating one move at a time.
|
||||
typename FeatureSet::IndexList removed, added;
|
||||
if constexpr (Forward)
|
||||
FeatureSet::append_changed_indices(perspective, ksq, target_state.diff, removed, added);
|
||||
if constexpr (std::is_same_v<FeatureSet, ThreatFeatureSet>)
|
||||
{
|
||||
const auto* pfBase = &featureTransformer.threatWeights[0];
|
||||
auto pfStride = static_cast<IndexType>(TransformedFeatureDimensions);
|
||||
if constexpr (Forward)
|
||||
FeatureSet::append_changed_indices(perspective, ksq, target_state.diff, removed, added,
|
||||
nullptr, false, pfBase, pfStride);
|
||||
else
|
||||
FeatureSet::append_changed_indices(perspective, ksq, computed.diff, added, removed,
|
||||
nullptr, false, pfBase, pfStride);
|
||||
}
|
||||
else
|
||||
FeatureSet::append_changed_indices(perspective, ksq, computed.diff, added, removed);
|
||||
{
|
||||
if constexpr (Forward)
|
||||
FeatureSet::append_changed_indices(perspective, ksq, target_state.diff, removed, added);
|
||||
else
|
||||
FeatureSet::append_changed_indices(perspective, ksq, computed.diff, added, removed);
|
||||
}
|
||||
|
||||
auto updateContext =
|
||||
make_accumulator_update_context(perspective, featureTransformer, computed, target_state);
|
||||
|
||||
Reference in New Issue
Block a user