From 339e1b49f619ceffa75019e196adf4de74b32cce Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sun, 24 Apr 2011 18:46:26 +0100 Subject: [PATCH] Don't allocate MAX_THREADS hash tables if not necessary 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 --- src/material.cpp | 2 +- src/search.cpp | 7 +++++-- src/thread.cpp | 44 +++++++++++++++++++++++++++++--------------- src/thread.h | 1 + src/tt.h | 5 ++++- 5 files changed, 40 insertions(+), 19 deletions(-) diff --git a/src/material.cpp b/src/material.cpp index ef7428a3..add0f32b 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -85,7 +85,7 @@ namespace { /// 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; } diff --git a/src/search.cpp b/src/search.cpp index bb61e2e9..751049cd 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -435,15 +435,18 @@ bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]) { UCIMultiPV = Options["MultiPV"].value(); SkillLevel = Options["Skill level"].value(); - ThreadsMgr.read_uci_options(); 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()); if (Options["Clear Hash"].value()) { Options["Clear Hash"].set_value("false"); TT.clear(); } - TT.set_size(Options["Hash"].value()); // 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. diff --git a/src/thread.cpp b/src/thread.cpp index 30c921d1..002819de 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -64,50 +64,48 @@ void ThreadsManager::read_uci_options() { activeThreads = Options["Threads"].value(); } + // 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; + int arg[MAX_THREADS]; // 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(); lock_init(&mpLock); - 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)); - - // 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]; - 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 - 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); #endif if (!ok) { @@ -150,6 +148,22 @@ void ThreadsManager::exit_threads() { } +// 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. diff --git a/src/thread.h b/src/thread.h index e576a5c4..e5a198c8 100644 --- a/src/thread.h +++ b/src/thread.h @@ -105,6 +105,7 @@ public: 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; } diff --git a/src/tt.h b/src/tt.h index a4ccd8be..69cdad0c 100644 --- a/src/tt.h +++ b/src/tt.h @@ -148,11 +148,14 @@ struct SimpleHash { void init() { + if (entries) + return; + 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)); -- 2.39.2