This prevent crashing on mobile devices with limited RAM,
currently with MAX_THREADS = 32 we would need 44MB that
could be too much for a poor cellphone.
No functional change.
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
/// MaterialInfoTable c'tor and d'tor allocate and free the space for Endgames
/// MaterialInfoTable c'tor and d'tor allocate and free the space for Endgames
-void MaterialInfoTable::init() { Base::init(); funcs = new Endgames(); }
+void MaterialInfoTable::init() { Base::init(); if (!funcs) funcs = new Endgames(); }
MaterialInfoTable::~MaterialInfoTable() { delete funcs; }
MaterialInfoTable::~MaterialInfoTable() { delete funcs; }
UCIMultiPV = Options["MultiPV"].value<int>();
SkillLevel = Options["Skill level"].value<int>();
UCIMultiPV = Options["MultiPV"].value<int>();
SkillLevel = Options["Skill level"].value<int>();
- ThreadsMgr.read_uci_options();
read_evaluation_uci_options(pos.side_to_move());
read_evaluation_uci_options(pos.side_to_move());
+ ThreadsMgr.read_uci_options();
+
+ // If needed allocate pawn and material hash tables and adjust TT size
+ ThreadsMgr.init_hash_tables();
+ TT.set_size(Options["Hash"].value<int>());
if (Options["Clear Hash"].value<bool>())
{
Options["Clear Hash"].set_value("false");
TT.clear();
}
if (Options["Clear Hash"].value<bool>())
{
Options["Clear Hash"].set_value("false");
TT.clear();
}
- TT.set_size(Options["Hash"].value<int>());
// Do we have to play with skill handicap? In this case enable MultiPV that
// we will use behind the scenes to retrieve a set of possible moves.
// Do we have to play with skill handicap? In this case enable MultiPV that
// we will use behind the scenes to retrieve a set of possible moves.
activeThreads = Options["Threads"].value<int>();
}
activeThreads = Options["Threads"].value<int>();
}
// init_threads() is called during startup. Initializes locks and condition
// variables and launches all threads sending them immediately to sleep.
void ThreadsManager::init_threads() {
// init_threads() is called during startup. Initializes locks and condition
// variables and launches all threads sending them immediately to sleep.
void ThreadsManager::init_threads() {
- int i, arg[MAX_THREADS];
- bool ok;
// This flag is needed to properly end the threads when program exits
allThreadsShouldExit = false;
// Threads will sent to sleep as soon as created, only main thread is kept alive
activeThreads = 1;
// This flag is needed to properly end the threads when program exits
allThreadsShouldExit = false;
// Threads will sent to sleep as soon as created, only main thread is kept alive
activeThreads = 1;
+ threads[0].state = THREAD_SEARCHING;
+
+ // Allocate pawn and material hash tables for main thread
+ init_hash_tables();
- for (i = 0; i < MAX_THREADS; i++)
+ // Initialize thread and split point locks
+ for (int i = 0; i < MAX_THREADS; i++)
- // Initialize thread and split point locks
lock_init(&threads[i].sleepLock);
cond_init(&threads[i].sleepCond);
for (int j = 0; j < MAX_ACTIVE_SPLIT_POINTS; j++)
lock_init(&(threads[i].splitPoints[j].lock));
lock_init(&threads[i].sleepLock);
cond_init(&threads[i].sleepCond);
for (int j = 0; j < MAX_ACTIVE_SPLIT_POINTS; j++)
lock_init(&(threads[i].splitPoints[j].lock));
-
- // All threads but first should be set to THREAD_INITIALIZING
- threads[i].state = (i == 0 ? THREAD_SEARCHING : THREAD_INITIALIZING);
-
- // Not in Threads c'tor to avoid global initialization order issues
- threads[i].pawnTable.init();
- threads[i].materialTable.init();
- // Create and startup the threads
- for (i = 1; i < MAX_THREADS; i++)
+ // Create and startup all the threads but the main that is already running
+ for (int i = 1; i < MAX_THREADS; i++)
+ threads[i].state = THREAD_INITIALIZING;
arg[i] = i;
#if !defined(_MSC_VER)
pthread_t pthread[1];
arg[i] = i;
#if !defined(_MSC_VER)
pthread_t pthread[1];
- ok = (pthread_create(pthread, NULL, init_thread, (void*)(&arg[i])) == 0);
+ bool ok = (pthread_create(pthread, NULL, init_thread, (void*)(&arg[i])) == 0);
pthread_detach(pthread[0]);
#else
pthread_detach(pthread[0]);
#else
- ok = (CreateThread(NULL, 0, init_thread, (LPVOID)(&arg[i]), 0, NULL) != NULL);
+ bool ok = (CreateThread(NULL, 0, init_thread, (LPVOID)(&arg[i]), 0, NULL) != NULL);
+// init_hash_tables() dynamically allocates pawn and material hash tables
+// according to the number of active threads. This avoids preallocating
+// memory for all possible threads if only few are used as, for instance,
+// on mobile devices where memory is scarce and allocating for MAX_THREADS
+// threads could even result in a crash.
+
+void ThreadsManager::init_hash_tables() {
+
+ for (int i = 0; i < activeThreads; i++)
+ {
+ threads[i].pawnTable.init();
+ threads[i].materialTable.init();
+ }
+}
+
+
// cutoff_at_splitpoint() checks whether a beta cutoff has occurred in
// the thread's currently active split point, or in some ancestor of
// the current split point.
// cutoff_at_splitpoint() checks whether a beta cutoff has occurred in
// the thread's currently active split point, or in some ancestor of
// the current split point.
Thread& operator[](int threadID) { return threads[threadID]; }
void init_threads();
void exit_threads();
Thread& operator[](int threadID) { return threads[threadID]; }
void init_threads();
void exit_threads();
+ void init_hash_tables();
int min_split_depth() const { return minimumSplitDepth; }
int active_threads() const { return activeThreads; }
int min_split_depth() const { return minimumSplitDepth; }
int active_threads() const { return activeThreads; }
+ if (entries)
+ return;
+
entries = new (std::nothrow) Entry[HashSize];
if (!entries)
{
std::cerr << "Failed to allocate " << HashSize * sizeof(Entry)
entries = new (std::nothrow) Entry[HashSize];
if (!entries)
{
std::cerr << "Failed to allocate " << HashSize * sizeof(Entry)
- << " bytes for material hash table." << std::endl;
+ << " bytes for hash table." << std::endl;
exit(EXIT_FAILURE);
}
memset(entries, 0, HashSize * sizeof(Entry));
exit(EXIT_FAILURE);
}
memset(entries, 0, HashSize * sizeof(Entry));