// Search depth at iteration 1
const Depth InitialDepth = OnePly;
- // Depth limit for selective search
- const Depth SelectiveDepth = 7 * OnePly;
-
// Use internal iterative deepening?
const bool UseIIDAtPVNodes = true;
const bool UseIIDAtNonPVNodes = true;
// remaining ones we will extend it.
const Value SingleReplyMargin = Value(0x20);
- // Margins for futility pruning in the quiescence search, and at frontier
- // and near frontier nodes.
- const Value FutilityMarginQS = Value(0x80);
+ // Depth limit for razoring
+ const Depth RazorDepth = 4 * OnePly;
- Value FutilityMargins[2 * PLY_MAX_PLUS_2]; // Initialized at startup.
+ /// Lookup tables initialized at startup
- // Each move futility margin is decreased
- const Value IncrementalFutilityMargin = Value(0x8);
+ // Reduction lookup tables and their getter functions
+ int8_t PVReductionMatrix[64][64]; // [depth][moveNumber]
+ int8_t NonPVReductionMatrix[64][64]; // [depth][moveNumber]
- // Depth limit for razoring
- const Depth RazorDepth = 4 * OnePly;
+ inline Depth pv_reduction(Depth d, int mn) { return (Depth) PVReductionMatrix[Min(d / 2, 63)][Min(mn, 63)]; }
+ inline Depth nonpv_reduction(Depth d, int mn) { return (Depth) NonPVReductionMatrix[Min(d / 2, 63)][Min(mn, 63)]; }
+
+ // Futility lookup tables and their getter functions
+ const Value FutilityMarginQS = Value(0x80);
+ int32_t FutilityMarginsMatrix[14][64]; // [depth][moveNumber]
+ int FutilityMoveCountArray[32]; // [depth]
+
+ inline Value futility_margin(Depth d, int mn) { return Value(d < 7*OnePly ? FutilityMarginsMatrix[Max(d, 0)][Min(mn, 63)] : 2 * VALUE_INFINITE); }
+ inline int futility_move_count(Depth d) { return d < 16*OnePly ? FutilityMoveCountArray[d] : 512; }
/// Variables initialized by UCI options
bool UseLogFile;
std::ofstream LogFile;
- // Reduction lookup tables and their getter functions
- // Initialized at startup
- int8_t PVReductionMatrix[64][64]; // [depth][moveNumber]
- int8_t NonPVReductionMatrix[64][64]; // [depth][moveNumber]
-
- inline Depth pv_reduction(Depth d, int mn) { return (Depth) PVReductionMatrix[Min(d / 2, 63)][Min(mn, 63)]; }
- inline Depth nonpv_reduction(Depth d, int mn) { return (Depth) NonPVReductionMatrix[Min(d / 2, 63)][Min(mn, 63)]; }
-
// MP related variables
int ActiveThreads = 1;
Depth MinimumSplitDepth;
Thread Threads[THREAD_MAX];
Lock MPLock;
Lock IOLock;
- bool AllThreadsShouldExit = false;
+ bool AllThreadsShouldExit, AllThreadsShouldSleep;
SplitPoint SplitPointStack[THREAD_MAX][ACTIVE_SPLIT_POINTS_MAX];
- bool Idle = true;
#if !defined(_MSC_VER)
pthread_cond_t WaitCond;
int maxNodes, int maxTime, Move searchMoves[]) {
// Initialize global search variables
- Idle = StopOnPonderhit = AbortSearch = Quit = false;
+ AllThreadsShouldSleep = StopOnPonderhit = AbortSearch = Quit = false;
AspirationFailLow = false;
NodesSincePoll = 0;
SearchStartTime = get_system_time();
if (UseLogFile)
LogFile.close();
- Idle = true;
+ AllThreadsShouldSleep = true;
return !Quit;
}
-/// init_threads() is called during startup. It launches all helper threads,
-/// and initializes the split point stack and the global locks and condition
-/// objects.
-
-void init_threads() {
+/// init_search() is called during startup. It initializes various lookup tables
- volatile int i;
- bool ok;
-
-#if !defined(_MSC_VER)
- pthread_t pthread[1];
-#endif
+void init_search() {
// Init our reduction lookup tables
- for (i = 1; i < 64; i++) // i == depth
+ for (int i = 1; i < 64; i++) // i == depth (OnePly = 1)
for (int j = 1; j < 64; j++) // j == moveNumber
{
double pvRed = 0.5 + log(double(i)) * log(double(j)) / 6.0;
}
// Init futility margins array
- FutilityMargins[0] = FutilityMargins[1] = Value(0);
+ for (int i = 0; i < 14; i++) // i == depth (OnePly = 2)
+ for (int j = 0; j < 64; j++) // j == moveNumber
+ {
+ FutilityMarginsMatrix[i][j] = (i < 2 ? 0 : 112 * bitScanReverse32(i * i / 2)) - 8 * j; // FIXME: test using log instead of BSR
+ }
- for (i = 2; i < 2 * PLY_MAX_PLUS_2; i++)
- {
- FutilityMargins[i] = Value(112 * bitScanReverse32(i * i / 2)); // FIXME: test using log instead of BSR
- }
+ // Init futility move count array
+ for (int i = 0; i < 32; i++) // i == depth (OnePly = 2)
+ FutilityMoveCountArray[i] = 3 + (1 << (3 * i / 8));
+}
- for (i = 0; i < THREAD_MAX; i++)
- Threads[i].activeSplitPoints = 0;
+
+/// init_threads() is called during startup. It launches all helper threads,
+/// and initializes the split point stack and the global locks and condition
+/// objects.
+
+void init_threads() {
+
+ volatile int i;
+ bool ok;
+
+#if !defined(_MSC_VER)
+ pthread_t pthread[1];
+#endif
// Initialize global locks
lock_init(&MPLock, NULL);
SitIdleEvent[i] = CreateEvent(0, FALSE, FALSE, 0);
#endif
+ // Will be set just before program exits to properly end the threads
+ AllThreadsShouldExit = false;
+
+ // Threads will be put to sleep as soon as created
+ AllThreadsShouldSleep = true;
+
// All threads except the main thread should be initialized to idle state
for (i = 1; i < THREAD_MAX; i++)
- {
- Threads[i].stop = false;
- Threads[i].workIsWaiting = false;
Threads[i].idle = true;
- Threads[i].running = false;
- }
// Launch the helper threads
for (i = 1; i < THREAD_MAX; i++)
void stop_threads() {
ActiveThreads = THREAD_MAX; // HACK
- Idle = false; // HACK
+ AllThreadsShouldSleep = false; // HACK
wake_sleeping_threads();
AllThreadsShouldExit = true;
for (int i = 1; i < THREAD_MAX; i++)
currentMove = threatMove = MOVE_NONE;
reduction = Depth(0);
eval = VALUE_NONE;
- evalInfo = NULL;
}
void SearchStack::initKillers() {
isCheck = pos.is_check();
- // Calculate depth dependant futility pruning parameters
- const int FutilityMoveCountMargin = 3 + (1 << (3 * int(depth) / 8));
-
// Evaluate the position statically
if (!isCheck)
{
if (tte && (tte->type() & VALUE_TYPE_EVAL))
staticValue = value_from_tt(tte->value(), ply);
else
- {
staticValue = evaluate(pos, ei, threadID);
- ss[ply].evalInfo = &ei;
- }
ss[ply].eval = staticValue;
- futilityValue = staticValue + FutilityMargins[int(depth)]; //FIXME: Remove me, only for split
+ futilityValue = staticValue + futility_margin(depth, 0); //FIXME: Remove me, only for split
staticValue = refine_eval(tte, staticValue, ply); // Enhance accuracy with TT value if possible
update_gains(pos, ss[ply - 1].currentMove, ss[ply - 1].eval, ss[ply].eval);
}
if ( !isCheck
&& allowNullmove
&& depth < RazorDepth
- && staticValue - FutilityMargins[int(depth)] >= beta)
- return staticValue - FutilityMargins[int(depth)];
+ && staticValue - futility_margin(depth, 0) >= beta)
+ return staticValue - futility_margin(depth, 0);
// Null move search
if ( allowNullmove
&& move != ttMove)
{
// Move count based pruning
- if ( moveCount >= FutilityMoveCountMargin
+ if ( moveCount >= futility_move_count(depth)
&& ok_to_prune(pos, move, ss[ply].threatMove)
&& bestValue > value_mated_in(PLY_MAX))
continue;
// Value based pruning
- Depth predictedDepth = newDepth;
+ Depth predictedDepth = newDepth - nonpv_reduction(depth, moveCount); //FIXME: We are ignoring condition: depth >= 3*OnePly, BUG??
+ futilityValueScaled = ss[ply].eval + futility_margin(predictedDepth, moveCount)
+ + H.gain(pos.piece_on(move_from(move)), move_to(move)) + 45;
- //FIXME: We are ignoring condition: depth >= 3*OnePly, BUG??
- ss[ply].reduction = nonpv_reduction(depth, moveCount);
- if (ss[ply].reduction)
- predictedDepth -= ss[ply].reduction;
-
- if (predictedDepth < SelectiveDepth)
+ if (futilityValueScaled < beta)
{
- int preFutilityValueMargin = 0;
- if (predictedDepth >= OnePly)
- preFutilityValueMargin = FutilityMargins[int(predictedDepth)];
-
- preFutilityValueMargin += H.gain(pos.piece_on(move_from(move)), move_to(move)) + 45;
-
- futilityValueScaled = ss[ply].eval + preFutilityValueMargin - moveCount * IncrementalFutilityMargin;
-
- if (futilityValueScaled < beta)
- {
- if (futilityValueScaled > bestValue)
- bestValue = futilityValueScaled;
- continue;
- }
+ if (futilityValueScaled > bestValue)
+ bestValue = futilityValueScaled;
+ continue;
}
}
Move move;
int moveCount;
bool isCheck = pos.is_check();
- bool useFutilityPruning = sp->depth < SelectiveDepth
+ bool useFutilityPruning = sp->depth < 7 * OnePly //FIXME: sync with search
&& !isCheck;
- const int FutilityMoveCountMargin = 3 + (1 << (3 * int(sp->depth) / 8));
-
while ( lock_grab_bool(&(sp->lock))
&& sp->bestValue < sp->beta
&& !thread_should_stop(threadID)
&& !captureOrPromotion)
{
// Move count based pruning
- if ( moveCount >= FutilityMoveCountMargin
+ if ( moveCount >= futility_move_count(sp->depth)
&& ok_to_prune(pos, move, ss[sp->ply].threatMove)
&& sp->bestValue > value_mated_in(PLY_MAX))
continue;
// Value based pruning
- Value futilityValueScaled = sp->futilityValue - moveCount * IncrementalFutilityMargin;
+ Value futilityValueScaled = sp->futilityValue - moveCount * 8; //FIXME: sync with search
if (futilityValueScaled < sp->beta)
{
Threads[threadID].running = true;
- while (true)
+ while (!AllThreadsShouldExit || threadID == 0)
{
- if (AllThreadsShouldExit && threadID != 0)
- break;
-
// If we are not thinking, wait for a condition to be signaled
// instead of wasting CPU time polling for work.
- while (threadID != 0 && (Idle || threadID >= ActiveThreads))
+ while ( threadID != 0
+ && !AllThreadsShouldExit
+ && (AllThreadsShouldSleep || threadID >= ActiveThreads))
{
+ Threads[threadID].sleeping = true;
+
#if !defined(_MSC_VER)
pthread_mutex_lock(&WaitLock);
if (Idle || threadID >= ActiveThreads)
#endif
}
+ // Out of the while loop to avoid races in case thread is woken up but
+ // while condition still holds true so that is put to sleep again.
+ Threads[threadID].sleeping = false;
+
// If this thread has been assigned work, launch a search
if (Threads[threadID].workIsWaiting)
{
{
for (int i = 1; i < ActiveThreads; i++)
{
+ assert(Threads[i].sleeping == true);
+
Threads[i].idle = true;
Threads[i].workIsWaiting = false;
}
for (int i = 1; i < THREAD_MAX; i++)
SetEvent(SitIdleEvent[i]);
#endif
+
+ // Wait for the threads to be all woken up
+ for (int i = 1; i < ActiveThreads; i++)
+ while (Threads[i].sleeping);
}
}