Skip to content

Commit

Permalink
More incremental accumulator updates
Browse files Browse the repository at this point in the history
This patch was inspired by c065abd which updates the accumulator,
if possible, based on the accumulator of two plies back if
the accumulator of the preceding ply is not available.

With this patch we look back even further in the position history
in an attempt to reduce the number of complete recomputations.
When we find a usable accumulator for the position N plies back,
we also update the accumulator of the position N-1 plies back
because that accumulator is most likely to be helpful later
when evaluating positions in sibling branches.
By not updating all intermediate accumulators immediately,
we avoid doing too much work that is not certain to be useful.
Overall, roughly 2-3% speedup.

This patch makes the code more specific to the net architecture,
changing input features of the net will require additional changes
to the incremental update code as discussed in the PR #3193 and #3191.

Passed STC:
https://tests.stockfishchess.org/tests/view/5f9056712c92c7fe3a8c60d0
LLR: 2.94 (-2.94,2.94) {-0.25,1.25}
Total: 10040 W: 1116 L: 968 D: 7956
Ptnml(0-2): 42, 722, 3365, 828, 63

closes #3193

No functional change.
  • Loading branch information
syzygy1 authored and vondele committed Oct 22, 2020
1 parent 258af8a commit 2046d5d
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 254 deletions.
108 changes: 0 additions & 108 deletions src/nnue/features/feature_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,90 +43,6 @@ namespace Eval::NNUE::Features {
template <typename Derived>
class FeatureSetBase {

public:
// Get a list of indices for active features
template <typename IndexListType>
static void AppendActiveIndices(
const Position& pos, TriggerEvent trigger, IndexListType active[2]) {

for (Color perspective : { WHITE, BLACK }) {
Derived::CollectActiveIndices(
pos, trigger, perspective, &active[perspective]);
}
}

// Get a list of indices for recently changed features
template <typename PositionType, typename IndexListType>
static void AppendChangedIndices(
const PositionType& pos, TriggerEvent trigger,
IndexListType removed[2], IndexListType added[2], bool reset[2]) {

auto collect_for_one = [&](const DirtyPiece& dp) {
for (Color perspective : { WHITE, BLACK }) {
switch (trigger) {
case TriggerEvent::kFriendKingMoved:
reset[perspective] = dp.piece[0] == make_piece(perspective, KING);
break;
default:
assert(false);
break;
}
if (reset[perspective]) {
Derived::CollectActiveIndices(
pos, trigger, perspective, &added[perspective]);
} else {
Derived::CollectChangedIndices(
pos, dp, trigger, perspective,
&removed[perspective], &added[perspective]);
}
}
};

auto collect_for_two = [&](const DirtyPiece& dp1, const DirtyPiece& dp2) {
for (Color perspective : { WHITE, BLACK }) {
switch (trigger) {
case TriggerEvent::kFriendKingMoved:
reset[perspective] = dp1.piece[0] == make_piece(perspective, KING)
|| dp2.piece[0] == make_piece(perspective, KING);
break;
default:
assert(false);
break;
}
if (reset[perspective]) {
Derived::CollectActiveIndices(
pos, trigger, perspective, &added[perspective]);
} else {
Derived::CollectChangedIndices(
pos, dp1, trigger, perspective,
&removed[perspective], &added[perspective]);
Derived::CollectChangedIndices(
pos, dp2, trigger, perspective,
&removed[perspective], &added[perspective]);
}
}
};

if (pos.state()->previous->accumulator.computed_accumulation) {
const auto& prev_dp = pos.state()->dirtyPiece;
if (prev_dp.dirty_num == 0) return;
collect_for_one(prev_dp);
} else {
const auto& prev_dp = pos.state()->previous->dirtyPiece;
if (prev_dp.dirty_num == 0) {
const auto& prev2_dp = pos.state()->dirtyPiece;
if (prev2_dp.dirty_num == 0) return;
collect_for_one(prev2_dp);
} else {
const auto& prev2_dp = pos.state()->dirtyPiece;
if (prev2_dp.dirty_num == 0) {
collect_for_one(prev_dp);
} else {
collect_for_two(prev_dp, prev2_dp);
}
}
}
}
};

// Class template that represents the feature set
Expand All @@ -146,30 +62,6 @@ namespace Eval::NNUE::Features {
CompileTimeList<TriggerEvent, FeatureType::kRefreshTrigger>;
static constexpr auto kRefreshTriggers = SortedTriggerSet::kValues;

private:
// Get a list of indices for active features
static void CollectActiveIndices(
const Position& pos, const TriggerEvent trigger, const Color perspective,
IndexList* const active) {
if (FeatureType::kRefreshTrigger == trigger) {
FeatureType::AppendActiveIndices(pos, perspective, active);
}
}

// Get a list of indices for recently changed features
static void CollectChangedIndices(
const Position& pos, const DirtyPiece& dp, const TriggerEvent trigger, const Color perspective,
IndexList* const removed, IndexList* const added) {

if (FeatureType::kRefreshTrigger == trigger) {
FeatureType::AppendChangedIndices(pos, dp, perspective, removed, added);
}
}

// Make the base class and the class template that recursively uses itself a friend
friend class FeatureSetBase<FeatureSet>;
template <typename... FeatureTypes>
friend class FeatureSet;
};

} // namespace Eval::NNUE::Features
Expand Down
5 changes: 4 additions & 1 deletion src/nnue/nnue_accumulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@

namespace Eval::NNUE {

// The accumulator of a StateInfo without parent is set to the INIT state
enum AccumulatorState { EMPTY, COMPUTED, INIT };

// Class that holds the result of affine transformation of input features
struct alignas(kCacheLineSize) Accumulator {
std::int16_t
accumulation[2][kRefreshTriggers.size()][kTransformedFeatureDimensions];
bool computed_accumulation;
AccumulatorState state[2];
};

} // namespace Eval::NNUE
Expand Down
Loading

0 comments on commit 2046d5d

Please sign in to comment.