Stockfish 1.8 - eval cache
Posted: Sun Jul 18, 2010 12:34 pm
One innovation in SF 1.8 is that static eval and king safety value is cached in TT.
Well, in this new code is a little bug. Whenever you enter a position not beeing in check, you should have a static eval value, either from TT or from evaluate(), everywhere in this node. But it's not always true. You can track the issue with assert() in search.cpp.
Step 9. Internal iterative deepening
When entering the IID block, first assert
Also in step 12. Value based futility pruning.
The assertions will fail. Why? Because sometimes you re-enter the node with the same ss. But every time you re-enter the node ss->eval will be cleared first. Now if you have a TT hit ss->eval will not be restored.
This can be fixed in search() by
and the same in qsearch() by
After fixing this I asked myself, why do you need to check for tte->static_value() != VALUE_NONE here?
It should not be neccesary, because now every time you enter a not-in-check node, the TT entry should have a static eval value. Right, but because TranspositionTable::insert_pv() does clear the cached static value, it is still necessary. Therefore I would suggest to also store the static eval value when writing back the PV to TT. Now all tests regarding tte->static_value() can be removed.
Well, in this new code is a little bug. Whenever you enter a position not beeing in check, you should have a static eval value, either from TT or from evaluate(), everywhere in this node. But it's not always true. You can track the issue with assert() in search.cpp.
Step 9. Internal iterative deepening
When entering the IID block, first assert
Code: Select all
if ( depth >= IIDDepth[PvNode]
&& ttMove == MOVE_NONE
&& (PvNode || (!isCheck && ss->eval >= beta - IIDMargin)))
{
assert(isCheck || ss->eval != VALUE_NONE);
...
Code: Select all
if ( !PvNode
&& !captureOrPromotion
&& !isCheck
&& !dangerous
&& move != ttMove
&& !move_is_castle(move))
{
assert(ss->eval != VALUE_NONE);
...
The assertions will fail. Why? Because sometimes you re-enter the node with the same ss. But every time you re-enter the node ss->eval will be cleared first. Now if you have a TT hit ss->eval will not be restored.
This can be fixed in search() by
Code: Select all
if (!PvNode && tte && ok_to_use_TT(tte, depth, beta, ply))
{
// Refresh tte entry to avoid aging
TT.store(posKey, tte->value(), tte->type(), tte->depth(), ttMove, tte->static_value(), tte->king_danger());
ss->currentMove = ttMove; // Can be MOVE_NONE
ss->eval = tte->static_value(); // here is the bug fix
return value_from_tt(tte->value(), ply);
}
Code: Select all
if (!PvNode && tte && ok_to_use_TT(tte, depth, beta, ply))
{
ss->currentMove = ttMove; // Can be MOVE_NONE
ss->eval = tte->static_value(); // here is the bug fix
return value_from_tt(tte->value(), ply);
}
After fixing this I asked myself, why do you need to check for tte->static_value() != VALUE_NONE here?
Code: Select all
// Step 5. Evaluate the position statically
// At PV nodes we do this only to update gain statistics
isCheck = pos.is_check();
if (!isCheck)
{
if (tte && tte->static_value() != VALUE_NONE)
{
ss->eval = tte->static_value();
ei.kingDanger[pos.side_to_move()] = tte->king_danger();
}
else
ss->eval = evaluate(pos, ei);
refinedValue = refine_eval(tte, ss->eval, ply); // Enhance accuracy with TT value if possible
update_gains(pos, (ss-1)->currentMove, (ss-1)->eval, ss->eval);
}