1
0
Fork 0
mirror of https://github.com/sockspls/badfish synced 2025-05-01 01:03:09 +00:00

Change the flow in wich moves are generated and picked

In MovePicker we get the next move with pick_move_from_list(),
then check if the return value is equal to MOVE_NONE and
in this case we update the state to the new phase.

This patch reorders the flow so that now from pick_move_from_list()
renamed get_next_move() we directly call go_next_phase() to
generate and sort the next bunch of moves when there are no more
move to try. This avoids to always check for pick_move_from_list()
returned value and the flow is more linear and natural.

Also use a local variable instead of a pointer dereferencing in a
time critical switch statement in get_next_move()

With this patch alone we have an incredible speed up of 3.2% !!!

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
This commit is contained in:
Marco Costalba 2009-08-27 09:12:51 +02:00
parent 129cde008c
commit 6cf28d4aa7
2 changed files with 175 additions and 186 deletions

View file

@ -43,12 +43,12 @@ namespace {
/// Variables /// Variables
CACHE_LINE_ALIGNMENT CACHE_LINE_ALIGNMENT
const MovegenPhaseT MainSearchPhaseTable[] = { PH_STOP, PH_NULL_MOVE, PH_TT_MOVES, PH_GOOD_CAPTURES, PH_KILLERS, PH_NONCAPTURES, PH_BAD_CAPTURES, PH_STOP}; const MovegenPhaseT MainSearchPhaseTable[] = { PH_NULL_MOVE, PH_TT_MOVES, PH_GOOD_CAPTURES, PH_KILLERS, PH_NONCAPTURES, PH_BAD_CAPTURES, PH_STOP};
const MovegenPhaseT MainSearchNoNullPhaseTable[] = { PH_STOP, PH_TT_MOVES, PH_GOOD_CAPTURES, PH_KILLERS, PH_NONCAPTURES, PH_BAD_CAPTURES, PH_STOP}; const MovegenPhaseT MainSearchNoNullPhaseTable[] = { PH_TT_MOVES, PH_GOOD_CAPTURES, PH_KILLERS, PH_NONCAPTURES, PH_BAD_CAPTURES, PH_STOP};
const MovegenPhaseT LowSearchPhaseTable[] = { PH_STOP, PH_TT_MOVES, PH_GOOD_CAPTURES, PH_NULL_MOVE, PH_KILLERS, PH_NONCAPTURES, PH_BAD_CAPTURES, PH_STOP}; const MovegenPhaseT LowSearchPhaseTable[] = { PH_TT_MOVES, PH_GOOD_CAPTURES, PH_NULL_MOVE, PH_KILLERS, PH_NONCAPTURES, PH_BAD_CAPTURES, PH_STOP};
const MovegenPhaseT EvasionsPhaseTable[] = { PH_STOP, PH_EVASIONS, PH_STOP}; const MovegenPhaseT EvasionsPhaseTable[] = { PH_EVASIONS, PH_STOP};
const MovegenPhaseT QsearchWithChecksPhaseTable[] = { PH_STOP, PH_TT_MOVES, PH_QCAPTURES, PH_QCHECKS, PH_STOP}; const MovegenPhaseT QsearchWithChecksPhaseTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_QCHECKS, PH_STOP};
const MovegenPhaseT QsearchWithoutChecksPhaseTable[] = { PH_STOP, PH_TT_MOVES, PH_QCAPTURES, PH_STOP}; const MovegenPhaseT QsearchWithoutChecksPhaseTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_STOP};
} }
@ -75,9 +75,14 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d,
} else } else
ttMoves[1] = killers[0] = killers[1] = MOVE_NONE; ttMoves[1] = killers[0] = killers[1] = MOVE_NONE;
movesPicked = numOfMoves = numOfBadCaptures = 0; numOfBadCaptures = 0;
finished = false; finished = false;
Color us = pos.side_to_move();
dc = p.discovered_check_candidates(us);
pinned = p.pinned_pieces(us);
if (p.is_check()) if (p.is_check())
phasePtr = EvasionsPhaseTable; phasePtr = EvasionsPhaseTable;
else if (d >= Depth(3 * OnePly)) else if (d >= Depth(3 * OnePly))
@ -89,120 +94,73 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d,
else else
phasePtr = QsearchWithoutChecksPhaseTable; phasePtr = QsearchWithoutChecksPhaseTable;
Color us = pos.side_to_move(); phasePtr--;
go_next_phase();
dc = p.discovered_check_candidates(us);
pinned = p.pinned_pieces(us);
} }
/// MovePicker::get_next_move() is the most important method of the MovePicker /// MovePicker::go_next_phase() generates, scores and sorts the next bunch
/// class. It returns a new legal move every time it is called, until there /// of moves when there are no more moves to try for the currrent phase.
/// are no more moves left of the types we are interested in.
Move MovePicker::get_next_move() { void MovePicker::go_next_phase() {
Move move; movesPicked = 0;
phase = *(++phasePtr);
switch (phase) {
while (true) case PH_NULL_MOVE:
{ case PH_TT_MOVES:
// If we already have a list of generated moves, pick the best move from return;
// the list, and return it.
move = pick_move_from_list();
if (move != MOVE_NONE)
{
assert(move_is_ok(move));
return move;
}
// Next phase case PH_GOOD_CAPTURES:
phasePtr++; numOfMoves = generate_captures(pos, moves);
switch (*phasePtr) { score_captures();
std::sort(moves, moves + numOfMoves);
return;
case PH_NULL_MOVE: case PH_KILLERS:
return MOVE_NULL; return;
case PH_TT_MOVES: case PH_NONCAPTURES:
movesPicked = 0; // This is used as index to ttMoves[] numOfMoves = generate_noncaptures(pos, moves);
break; score_noncaptures();
std::sort(moves, moves + numOfMoves);
return;
case PH_GOOD_CAPTURES: case PH_BAD_CAPTURES:
numOfMoves = generate_captures(pos, moves); // Bad captures SEE value is already calculated so just sort them
score_captures(); // to get SEE move ordering.
std::sort(moves, moves + numOfMoves); std::sort(badCaptures, badCaptures + numOfBadCaptures);
movesPicked = 0; return;
break;
case PH_KILLERS: case PH_EVASIONS:
movesPicked = 0; // This is used as index to killers[] assert(pos.is_check());
break; numOfMoves = generate_evasions(pos, moves, pinned);
score_evasions();
std::sort(moves, moves + numOfMoves);
return;
case PH_NONCAPTURES: case PH_QCAPTURES:
numOfMoves = generate_noncaptures(pos, moves); numOfMoves = generate_captures(pos, moves);
score_noncaptures(); score_captures();
std::sort(moves, moves + numOfMoves); std::sort(moves, moves + numOfMoves);
movesPicked = 0; return;
break;
case PH_BAD_CAPTURES: case PH_QCHECKS:
// Bad captures SEE value is already calculated so just sort them // Perhaps we should order moves move here? FIXME
// to get SEE move ordering. numOfMoves = generate_non_capture_checks(pos, moves, dc);
std::sort(badCaptures, badCaptures + numOfBadCaptures); return;
movesPicked = 0;
break;
case PH_EVASIONS: case PH_STOP:
assert(pos.is_check()); return;
numOfMoves = generate_evasions(pos, moves, pinned);
score_evasions();
std::sort(moves, moves + numOfMoves);
movesPicked = 0;
break;
case PH_QCAPTURES: default:
numOfMoves = generate_captures(pos, moves); assert(false);
score_captures(); return;
std::sort(moves, moves + numOfMoves);
movesPicked = 0;
break;
case PH_QCHECKS:
// Perhaps we should order moves move here? FIXME
numOfMoves = generate_non_capture_checks(pos, moves, dc);
movesPicked = 0;
break;
case PH_STOP:
return MOVE_NONE;
default:
assert(false);
return MOVE_NONE;
}
} }
} }
/// A variant of get_next_move() which takes a lock as a parameter, used to
/// prevent multiple threads from picking the same move at a split point.
Move MovePicker::get_next_move(Lock &lock) {
lock_grab(&lock);
if (finished)
{
lock_release(&lock);
return MOVE_NONE;
}
Move m = get_next_move();
if (m == MOVE_NONE)
finished = true;
lock_release(&lock);
return m;
}
/// MovePicker::score_captures(), MovePicker::score_noncaptures(), /// MovePicker::score_captures(), MovePicker::score_noncaptures(),
/// MovePicker::score_evasions() and MovePicker::score_qcaptures() assign a /// MovePicker::score_evasions() and MovePicker::score_qcaptures() assign a
/// numerical move ordering score to each move in a move list. The moves /// numerical move ordering score to each move in a move list. The moves
@ -276,100 +234,131 @@ void MovePicker::score_evasions() {
} }
} }
/// MovePicker::get_next_move() is the most important method of the MovePicker
/// class. It returns a new legal move every time it is called, until there
/// are no more moves left.
/// It picks the move with the biggest score from a list of generated moves taking
/// care not to return the tt move if that has already been serched previously.
/// MovePicker::pick_move_from_list() picks the move with the biggest score Move MovePicker::get_next_move() {
/// from a list of generated moves (moves[] or badCaptures[], depending on
/// the current move generation phase). It takes care not to return the
/// transposition table move if that has already been serched previously.
Move MovePicker::pick_move_from_list() {
assert(movesPicked >= 0); assert(movesPicked >= 0);
assert(!pos.is_check() || *phasePtr == PH_EVASIONS || *phasePtr == PH_STOP); assert(!pos.is_check() || *phasePtr == PH_EVASIONS || *phasePtr == PH_STOP);
assert( pos.is_check() || *phasePtr != PH_EVASIONS); assert( pos.is_check() || *phasePtr != PH_EVASIONS);
switch (*phasePtr) { while (true)
{
switch (phase) {
case PH_TT_MOVES: case PH_NULL_MOVE:
while (movesPicked < 2) { go_next_phase();
Move move = ttMoves[movesPicked++]; return MOVE_NULL;
if ( move != MOVE_NONE
&& move_is_legal(pos, move, pinned))
return move;
}
break;
case PH_GOOD_CAPTURES: case PH_TT_MOVES:
while (movesPicked < numOfMoves) while (movesPicked < 2) {
{ Move move = ttMoves[movesPicked++];
Move move = moves[movesPicked++].move; if ( move != MOVE_NONE
if ( move != ttMoves[0] && move_is_legal(pos, move, pinned))
&& move != ttMoves[1] return move;
&& pos.pl_move_is_legal(move, pinned)) }
break;
case PH_GOOD_CAPTURES:
while (movesPicked < numOfMoves)
{ {
// Check for a non negative SEE now Move move = moves[movesPicked++].move;
int seeValue = pos.see_sign(move); if ( move != ttMoves[0]
if (seeValue >= 0) && move != ttMoves[1]
return move; && pos.pl_move_is_legal(move, pinned))
{
// Check for a non negative SEE now
int seeValue = pos.see_sign(move);
if (seeValue >= 0)
return move;
// Losing capture, move it to the badCaptures[] array, note // Losing capture, move it to the badCaptures[] array, note
// that move has now been already checked for legality. // that move has now been already checked for legality.
assert(numOfBadCaptures < 63); assert(numOfBadCaptures < 63);
badCaptures[numOfBadCaptures].move = move; badCaptures[numOfBadCaptures].move = move;
badCaptures[numOfBadCaptures++].score = seeValue; badCaptures[numOfBadCaptures++].score = seeValue;
}
} }
break;
case PH_KILLERS:
while (movesPicked < 2) {
Move move = killers[movesPicked++];
if ( move != MOVE_NONE
&& move != ttMoves[0]
&& move != ttMoves[1]
&& move_is_legal(pos, move, pinned)
&& !pos.move_is_capture(move))
return move;
}
break;
case PH_NONCAPTURES:
while (movesPicked < numOfMoves)
{
Move move = moves[movesPicked++].move;
if ( move != ttMoves[0]
&& move != ttMoves[1]
&& move != killers[0]
&& move != killers[1]
&& pos.pl_move_is_legal(move, pinned))
return move;
}
break;
case PH_EVASIONS:
if (movesPicked < numOfMoves)
return moves[movesPicked++].move;
break;
case PH_BAD_CAPTURES:
if (movesPicked < numOfBadCaptures)
return badCaptures[movesPicked++].move;
break;
case PH_QCAPTURES:
case PH_QCHECKS:
while (movesPicked < numOfMoves)
{
Move move = moves[movesPicked++].move;
// Maybe postpone the legality check until after futility pruning?
if ( move != ttMoves[0]
&& pos.pl_move_is_legal(move, pinned))
return move;
}
break;
case PH_STOP:
return MOVE_NONE;
default:
assert(false);
break;
} }
break; go_next_phase();
case PH_KILLERS:
while (movesPicked < 2) {
Move move = killers[movesPicked++];
if ( move != MOVE_NONE
&& move != ttMoves[0]
&& move != ttMoves[1]
&& move_is_legal(pos, move, pinned)
&& !pos.move_is_capture(move))
return move;
}
break;
case PH_NONCAPTURES:
while (movesPicked < numOfMoves)
{
Move move = moves[movesPicked++].move;
if ( move != ttMoves[0]
&& move != ttMoves[1]
&& move != killers[0]
&& move != killers[1]
&& pos.pl_move_is_legal(move, pinned))
return move;
}
break;
case PH_EVASIONS:
if (movesPicked < numOfMoves)
return moves[movesPicked++].move;
break;
case PH_BAD_CAPTURES:
if (movesPicked < numOfBadCaptures)
return badCaptures[movesPicked++].move;
break;
case PH_QCAPTURES:
case PH_QCHECKS:
while (movesPicked < numOfMoves)
{
Move move = moves[movesPicked++].move;
// Maybe postpone the legality check until after futility pruning?
if ( move != ttMoves[0]
&& pos.pl_move_is_legal(move, pinned))
return move;
}
break;
default:
break;
} }
return MOVE_NONE; return MOVE_NONE;
} }
/// A variant of get_next_move() which takes a lock as a parameter, used to
/// prevent multiple threads from picking the same move at a split point.
Move MovePicker::get_next_move(Lock &lock) {
lock_grab(&lock);
if (finished)
{
lock_release(&lock);
return MOVE_NONE;
}
Move m = get_next_move();
if (m == MOVE_NONE)
finished = true;
lock_release(&lock);
return m;
}

View file

@ -75,13 +75,13 @@ private:
void score_captures(); void score_captures();
void score_noncaptures(); void score_noncaptures();
void score_evasions(); void score_evasions();
Move pick_move_from_list(); void go_next_phase();
const Position& pos; const Position& pos;
const History& H; const History& H;
Move ttMoves[2], killers[2]; Move ttMoves[2], killers[2];
const MovegenPhaseT* phasePtr; const MovegenPhaseT* phasePtr;
int movesPicked, numOfMoves, numOfBadCaptures; int phase, movesPicked, numOfMoves, numOfBadCaptures;
bool finished; bool finished;
Bitboard dc, pinned; Bitboard dc, pinned;
MoveStack moves[256], badCaptures[64]; MoveStack moves[256], badCaptures[64];