00001
00002
00003 #ifdef OSL_SMP
00004
00005 #include "osl/search/alphaBeta2Parallel.h"
00006 #include "osl/search/simpleHashTable.h"
00007 #include "osl/search/usiReporter.h"
00008 #include "osl/misc/nonBlockDelete.h"
00009 #include <boost/foreach.hpp>
00010 #include <iostream>
00011 #include <bitset>
00012 #ifdef _WIN32
00013 # include <malloc.h>
00014 #endif
00015
00016 #define DEBUG_SMP 0
00017 #define ONLY_HELP_DESCENDANT
00018
00019
00020
00021 struct osl::search::AlphaBeta2ParallelCommon::LivingThreadLock
00022 {
00023 AlphaBeta2ParallelCommon *shared;
00024 LivingThreadLock(AlphaBeta2ParallelCommon *s) : shared(s)
00025 {
00026 boost::mutex::scoped_lock lk(shared->living_threads_lock);
00027 shared->living_threads += 1;
00028 shared->living_threads_condition.notify_one();
00029 }
00030 ~LivingThreadLock()
00031 {
00032 boost::mutex::scoped_lock lk(shared->living_threads_lock);
00033 shared->living_threads -= 1;
00034 shared->living_threads_condition.notify_one();
00035 }
00036 };
00037
00038
00039 template <class EvalT>
00040 osl::search::AlphaBeta2Parallel<EvalT>::
00041 Worker::Worker(int tid, AlphaBeta2Parallel *s) : shared(s), thread_id(tid)
00042 {
00043 }
00044
00045 template <class EvalT>
00046 void
00047 #ifdef __GNUC__
00048 # ifdef _WIN32
00049 __attribute__((noinline))
00050 __attribute__((force_align_arg_pointer))
00051 # endif
00052 #endif
00053 osl::search::AlphaBeta2Parallel<EvalT>::Worker::operator()()
00054 {
00055 #if DEBUG_SMP > 1
00056 {
00057 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00058 std::cerr << "thread " << thread_id << " started\n";
00059 }
00060 #endif
00061 try {
00062 AlphaBeta2ParallelCommon::LivingThreadLock lk(shared);
00063 shared->threadWait(thread_id, -1);
00064 }
00065 catch (std::exception& e) {
00066 std::cerr << "warning caught exception in thread root " << e.what() << "\n";
00067 }
00068 catch (...) {
00069 std::cerr << "warning caught unknown exception in thread root\n";
00070 }
00071 }
00072
00073
00074
00075 static osl::misc::AtomicCounter parallel_counter;
00076
00077 osl::search::AlphaBeta2ParallelCommon::
00078 AlphaBeta2ParallelCommon()
00079 : smp_idle(0), quit(false),
00080 parallel_splits(0), max_split_depth(0), descendant_reject(0), descendant_test(0),
00081 living_threads(0), max_threads(OslConfig::numCPUs()), max_thread_group(5),
00082 split_min_limit(400), my_id(parallel_counter.valueAndinc()), started(false)
00083 {
00084 job.fill(0);
00085 info[0].thread_id = 0;
00086 info[0].used = true;
00087 info[0].parent = -1;
00088 waiting.fill(0);
00089
00090 threads.fill(0);
00091 checkmate.fill(0);
00092 for (int i=1; i<max_threads; ++i) {
00093 threads[i] = 0;
00094 checkmate[i] = 0;
00095 }
00096 }
00097
00098 osl::search::AlphaBeta2ParallelCommon::
00099 ~AlphaBeta2ParallelCommon()
00100 {
00101 waitAll();
00102 for (int i=1; i<max_threads; ++i) {
00103 delete checkmate[i];
00104 }
00105 #if DEBUG_SMP > 1
00106 std::cerr << "<= AlphaBeta2Parallel " << my_id << "\n";
00107 #endif
00108 #if DEBUG_SMP > 2
00109 std::cerr << "descendant_reject " << descendant_reject
00110 << " / " << descendant_test << " = " << (double)descendant_reject/descendant_test << "\n";
00111 std::cerr << "max_split_depth " << max_split_depth << "\n";
00112 #endif
00113 }
00114
00115 void osl::search::
00116 AlphaBeta2ParallelCommon::waitAll() {
00117 #ifndef OSL_BUSY_WAIT
00118 {
00119 boost::mutex::scoped_lock lk(lock_smp);
00120 #endif
00121 quit = true;
00122 started = false;
00123 #ifndef OSL_BUSY_WAIT
00124 condition_smp.notify_all();
00125 }
00126 #endif
00127 boost::mutex::scoped_lock lk(living_threads_lock);
00128 while (living_threads != (quit ? 0 : smp_idle)) {
00129 living_threads_condition.wait(lk);
00130 }
00131 for (int i=1; i<max_threads; ++i) {
00132 delete threads[i];
00133 threads[i] = 0;
00134 }
00135 }
00136
00137 bool osl::search::AlphaBeta2ParallelCommon::isDescendant(int elder, int younger)
00138 {
00139 #ifndef ONLY_HELP_DESCENDANT
00140 return true;
00141 #else
00142 ++descendant_test;
00143 if (elder < 0)
00144 return true;
00145 while (younger >= 0) {
00146 if (elder == younger)
00147 return true;
00148 younger = info[younger].parent;
00149 }
00150 ++descendant_reject;
00151 return false;
00152 #endif
00153 }
00154
00155
00156
00157 template <class EvalT>
00158 osl::search::AlphaBeta2Parallel<EvalT>::
00159 AlphaBeta2Parallel(AlphaBeta2Tree<EvalT> *m)
00160 : AlphaBeta2ParallelCommon(), master(m)
00161 {
00162 #if DEBUG_SMP > 0
00163 std::cerr << "=> AlphaBeta2Parallel " << max_threads << " threads ";
00164 # if DEBUG_SMP > 1
00165 std::cerr << " id " << my_id;
00166 # endif
00167 std::cerr << "\n";
00168 #endif
00169 tree.fill(0);
00170 tree[0] = master;
00171 checkmate[0] = checkmateSearcher(*master);
00172 }
00173
00174 template <class EvalT>
00175 osl::search::AlphaBeta2Parallel<EvalT>::
00176 ~AlphaBeta2Parallel()
00177 {
00178 }
00179
00180 template <class EvalT>
00181 void osl::search::AlphaBeta2Parallel<EvalT>::
00182 threadStart()
00183 {
00184 if (started)
00185 return;
00186 started = true;
00187 quit = false;
00188 int i=1;
00189 for (; i<max_threads; ++i) {
00190 int j=0, max_retry=4;
00191 for (; j<max_retry; ++j) {
00192 try
00193 {
00194 if (! checkmate[i])
00195 checkmate[i] = new checkmate_t(master->checkmateSearcher());
00196 threads[i] = new boost::thread(Worker(i, this));
00197 break;
00198 }
00199 catch (std::exception& e)
00200 {
00201 std::cerr << e.what() << "\n";
00202 }
00203 std::cerr << "wait for thread " << i << " started\n";
00204 const int microseconds = (j+1)*100000;
00205 #ifdef _WIN32
00206 boost::this_thread::sleep(boost::posix_time::microseconds(microseconds));
00207 #else
00208 usleep(microseconds);
00209 #endif
00210 NonBlockDelete::deleteAll();
00211 }
00212 if (j == max_retry)
00213 break;
00214 }
00215 if (i < max_threads)
00216 {
00217 std::cerr << "error in start thread #" << i << "\n";
00218 for (int j=i; j<max_threads; ++j) {
00219 delete checkmate[j];
00220 checkmate[j] = 0;
00221 }
00222 max_threads = i;
00223 }
00224 boost::mutex::scoped_lock lk(living_threads_lock);
00225 while (living_threads+1 != max_threads) {
00226 living_threads_condition.wait(lk);
00227 }
00228 }
00229
00230 template <class EvalT>
00231 void osl::search::AlphaBeta2Parallel<EvalT>::
00232 search(int tree_id)
00233 {
00234 TreeInfo *info = &this->info[tree_id];
00235 assert(tree[tree_id]);
00236 if (info->is_root)
00237 tree[tree_id]->examineMovesRootPar(tree_id);
00238 else
00239 tree[tree_id]->examineMovesOther(tree_id);
00240 }
00241
00242 template <class EvalT>
00243 int osl::search::
00244 AlphaBeta2Parallel<EvalT>::treeId(AlphaBeta2Tree<EvalT> *t)
00245 {
00246 if (t == master)
00247 return 0;
00248 for (size_t i=1; i<tree.size(); ++i)
00249 if (t == tree[i])
00250 return i;
00251 assert(0);
00252 abort();
00253 }
00254
00255 template <class EvalT>
00256 void osl::search::AlphaBeta2Parallel<EvalT>::
00257 threadWait(int thread_id, int waiting)
00258 {
00259 #if DEBUG_SMP > 2
00260 {
00261 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00262 std::cerr << "thread " << thread_id << " ready, waiting " << waiting << "\n";
00263 }
00264 #endif
00265 while (1) {
00266 this->waiting[thread_id] = waiting;
00267 {
00268 boost::mutex::scoped_lock lk(lock_smp);
00269 smp_idle++;
00270 }
00271 {
00272 #ifndef OSL_BUSY_WAIT
00273 boost::mutex::scoped_lock lk(lock_smp);
00274 #endif
00275 while (! job[thread_id]
00276 && ! quit
00277 && (waiting < 0 || info[waiting].nprocs))
00278 {
00279 #ifndef OSL_BUSY_WAIT
00280 condition_smp.wait(lk);
00281 #endif
00282 }
00283
00284 if (quit) {
00285 {
00286 #ifdef OSL_BUSY_WAIT
00287 boost::mutex::scoped_lock lk(lock_smp);
00288 #endif
00289 --smp_idle;
00290 }
00291 #if DEBUG_SMP > 1
00292 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00293 std::cerr << "thread " << thread_id << " exiting\n";
00294 #endif
00295 return;
00296 }
00297 {
00298 #ifdef OSL_BUSY_WAIT
00299 boost::mutex::scoped_lock lk(lock_smp);
00300 #endif
00301 if (! job[thread_id])
00302 job[thread_id] = waiting;
00303 --smp_idle;
00304 }
00305 }
00306
00307 if (job[thread_id] == waiting) {
00308 #ifndef NDEBUG
00309 if (waiting >= 0) {
00310 for (int i=0; i<max_threads; ++i) {
00311 assert(info[waiting].siblings[i] == 0);
00312 }
00313 assert(info[waiting].nprocs == 0);
00314 }
00315 #endif
00316 #if DEBUG_SMP > 3
00317 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00318 std::cerr << "thread " << thread_id << " go up "
00319 << waiting << " " << info[job[thread_id]].best_move << "\n";
00320 #endif
00321 return;
00322 }
00323
00324 if (quit || job[thread_id] == -1) {
00325 return;
00326 }
00327 int my_job = job[thread_id];
00328 #if DEBUG_SMP > 3
00329 {
00330 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00331 std::cerr << "thread " << thread_id << " go to job " << my_job << " waiting " << waiting << "\n";
00332 if (! tree[my_job]) {
00333 std::cerr << "thread " << thread_id << " null job " << my_job << " waiting " << waiting << "\n";
00334 }
00335 }
00336 #endif
00337
00338 assert(tree[my_job]);
00339 search(my_job);
00340
00341 int parent = info[my_job].parent;
00342 boost::mutex::scoped_lock lk(lock_smp);
00343 {
00344 SCOPED_LOCK(lk,info[parent].lock);
00345 copyToParent(parent, my_job);
00346 info[parent].nprocs--;
00347 info[parent].siblings[thread_id] = 0;
00348 #ifndef OSL_BUSY_WAIT
00349 if (info[parent].nprocs == 0)
00350 condition_smp.notify_all();
00351 #endif
00352 }
00353 job[thread_id] = 0;
00354 delete tree[my_job];
00355 tree[my_job] = 0;
00356 #if DEBUG_SMP > 3
00357 {
00358 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00359 std::cerr << "thread " << thread_id << " back from job " << my_job << " waiting " << waiting;
00360 if (waiting >= 0)
00361 std::cerr << " rest " << info[waiting].nprocs;
00362 std::cerr << "\n";
00363 }
00364 #endif
00365 }
00366 }
00367
00368 template <class EvalT>
00369 bool osl::search::AlphaBeta2Parallel<EvalT>::
00370 split(AlphaBeta2Tree<EvalT> *tree, int tree_id, int thread_id, int max_split)
00371 {
00372 TreeInfo *pinfo = &info[tree_id];
00373 #if DEBUG_SMP > 2
00374 {
00375 unsigned int depth = 0;
00376 int parent = pinfo->parent;
00377 while (parent >= 0)
00378 ++depth, parent = info[parent].parent;;
00379 max_split_depth = std::max(depth, max_split_depth);
00380 }
00381 for (int i=0; i<max_threads; ++i) {
00382 assert(pinfo->siblings[i] == 0);
00383 }
00384 #endif
00385 assert(tree == master || tree == this->tree[tree_id]);
00386 {
00387 boost::mutex::scoped_lock lk(lock_smp);
00388 {
00389 int tid=0;
00390 for (; tid<max_threads && job[tid]; ++tid)
00391 ;
00392 if (tid == max_threads || tree->stop_tree)
00393 return false;
00394 }
00395
00396 parallel_splits++;
00397 job[pinfo->thread_id] = 0;
00398 pinfo->nprocs = 0;
00399
00400 int nblocks = 0;
00401 if (const int child_id = copyToChild(tree_id, thread_id))
00402 {
00403
00404 nblocks++;
00405 pinfo->siblings[thread_id] = child_id;
00406 info[child_id].thread_id = thread_id;
00407 info[child_id].parent = tree_id;
00408 pinfo->nprocs++;
00409 }
00410 if (max_split <= 0)
00411 max_split = std::max(max_threads/2, max_thread_group);
00412 else
00413 max_split = std::min(max_split, std::max(max_threads/2, max_thread_group));
00414 for (int tid = 0;
00415 tid < max_threads && nblocks < max_split;
00416 ++tid) {
00417 assert(pinfo->siblings[tid] == 0 || tid == thread_id);
00418 if (job[tid] || tid == thread_id)
00419 continue;
00420 if (! isDescendant(waiting[tid], pinfo->parent))
00421 continue;
00422 int child_id = copyToChild(tree_id, tid);
00423 if (!child_id)
00424 continue;
00425 #if DEBUG_SMP > 3
00426 {
00427 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00428 std::cerr << "split " << tree_id << " in " << thread_id << " => " << child_id << " in " << tid << "\n";
00429 }
00430 #endif
00431 nblocks++;
00432 pinfo->siblings[tid] = child_id;
00433 info[child_id].thread_id = tid;
00434 info[child_id].parent = tree_id;
00435 pinfo->nprocs++;
00436 }
00437 pinfo->search_value = pinfo->value;
00438
00439 if (!nblocks) {
00440 job[pinfo->thread_id] = tree_id;
00441 return false;
00442 }
00443
00444 for (int tid=0; tid< max_threads; ++tid)
00445 if (pinfo->siblings[tid])
00446 job[tid] = pinfo->siblings[tid];
00447 }
00448 #ifndef OSL_BUSY_WAIT
00449 condition_smp.notify_all();
00450 #endif
00451 threadWait(pinfo->thread_id, tree_id);
00452
00453 return true;
00454 }
00455
00456 template <class EvalT>
00457 void osl::search::
00458 AlphaBeta2Parallel<EvalT>::stopThread(int tree_id)
00459 {
00460 TreeInfo *info = &this->info[tree_id];
00461 AlphaBeta2Tree<EvalT> *tree = this->tree[tree_id];
00462 SCOPED_LOCK(lk,info->lock);
00463 tree->stop_tree = true;
00464 for (int tid = 0; tid<max_threads; tid++)
00465 if (info->siblings[tid])
00466 stopThread(info->siblings[tid]);
00467 }
00468
00469 template <class EvalT>
00470 void osl::search::
00471 AlphaBeta2Parallel<EvalT>::copyToParent(int parent, int child)
00472 {
00473 TreeInfo *c = &info[child];
00474 AlphaBeta2Tree<EvalT> *cc = tree[child], *pp = tree[parent];
00475 c->used = 0;
00476 pp->node_count += cc->nodeCount();
00477 pp->mpn.merge(cc->mpn);
00478 pp->mpn_cut.merge(cc->mpn_cut);
00479 pp->alpha_update.merge(cc->alpha_update);
00480 pp->last_alpha_update.merge(cc->last_alpha_update);
00481 pp->ext.merge(cc->ext);
00482 pp->ext_limit.merge(cc->ext_limit);
00483 }
00484
00485 template <class EvalT>
00486 int osl::search::
00487 AlphaBeta2Parallel<EvalT>::copyToChild(int parent, int thread_id)
00488 {
00489 static int warnings = 0;
00490 int first = thread_id * MaxBlocksPerCpu + 1;
00491 int last = first + MaxBlocksPerCpu;
00492 int maxb = max_threads * MaxBlocksPerCpu + 1;
00493
00494 int cid=first;
00495 for (; cid < last && info[cid].used; cid++)
00496 ;
00497
00498 if (cid >= last) {
00499 if (++warnings < 6) {
00500 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00501 std::cerr << "WARNING. optimal SMP block cannot be allocated, thread "
00502 << thread_id << "\n";
00503 }
00504 for (cid=1; cid<maxb && info[cid].used; cid++)
00505 ;
00506 if (cid >= maxb) {
00507 if (warnings < 6) {
00508 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00509 std::cerr << "ERROR. no SMP block can be allocated\n";
00510 }
00511 return 0;
00512 }
00513 }
00514
00515 TreeInfo *c = &info[cid], *p = &info[parent];
00516 try
00517 {
00518 assert(tree[cid] == 0);
00519 tree[cid] = new AlphaBeta2Tree<EvalT>(*tree[parent], this);
00520 }
00521 catch (std::bad_alloc&)
00522 {
00523 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00524 std::cerr << "ERROR. split failed due to bad_alloc\n";
00525 return 0;
00526 }
00527 c->set(*p, max_threads);
00528 tree[cid]->setCheckmateSearcher(checkmate[thread_id]);
00529
00530 return cid;
00531 }
00532
00533 template <class EvalT>
00534 const std::pair<osl::MoveLogProb,size_t> osl::search::
00535 AlphaBeta2Parallel<EvalT>::nextMove(int tree_id)
00536 {
00537 int parent = info[tree_id].parent;
00538 TreeInfo *info = &this->info[parent];
00539 SCOPED_LOCK(lk,info->lock);
00540 const size_t old_index = info->move_index;
00541 if (tree[parent]->stop_tree)
00542 return std::make_pair(MoveLogProb(), old_index);
00543 if (info->is_root) {
00544 if (old_index < info->moves.size()) {
00545 ++(info->move_index);
00546 return std::make_pair(info->moves[old_index], old_index);
00547 }
00548 return std::make_pair(MoveLogProb(), old_index);
00549 }
00550 else {
00551 MoveLogProb m = (info->turn == BLACK)
00552 ? tree[parent]->template nextMove<BLACK>()
00553 : tree[parent]->template nextMove<WHITE>();
00554 if (m.validMove()) {
00555 assert(m.player() == info->turn);
00556 ++(info->move_index);
00557 }
00558 return std::make_pair(m, old_index);
00559 }
00560 }
00561
00562 template <class EvalT>
00563 size_t osl::search::
00564 AlphaBeta2Parallel<EvalT>::checkmateCount() const
00565 {
00566 return master->checkmateSearcher().totalNodeCount();
00567 }
00568
00569 template <class EvalT>
00570 size_t osl::search::
00571 AlphaBeta2Parallel<EvalT>::mainCheckmateCount() const
00572 {
00573 return master->checkmateSearcher().mainNodeCount();
00574 }
00575
00576
00577
00578 template <class EvalT>
00579 template <osl::Player P>
00580 void osl::search::
00581 AlphaBeta2Tree<EvalT>::testMoveRoot(int tree_id, const MoveLogProb& m)
00582 {
00583 if (stop_tree) {
00584 std::cerr << "root tree stop\n";
00585 return;
00586 }
00587
00588 Window w;
00589 AlphaBeta2ParallelCommon::TreeInfo *parent = shared->parent(tree_id);
00590 {
00591 SCOPED_LOCK(lk,parent->lock);
00592 w = parent->window;
00593 assert(w.isConsistent());
00594 }
00595 assert(P == m.player());
00596 #ifndef GPSONE
00597 if (this->multi_pv) {
00598 int width = this->multi_pv*this->eval.captureValue(newPtypeO(P, PAWN))/200;
00599 if (width % 2 == 0)
00600 width -= EvalTraits<P>::delta;
00601 w.alpha(P) = parent->search_value + width;
00602 }
00603 #endif
00604 const int result = alphaBetaSearch<P>(m, w, false);
00605
00606 if (eval::betterThan(P, result, parent->search_value)) {
00607 makePV(m.move());
00608 if (eval::betterThan(P, result, w.beta(P))) {
00609 {
00610 boost::mutex::scoped_lock lk_smp(shared->lock_smp);
00611 SCOPED_LOCK(lk,parent->lock);
00612 if (! stop_tree) {
00613 #if DEBUG_SMP > 2
00614 std::cerr << "beta cut root " << tree_id << "\n";
00615 #endif
00616 for (int tid=0; tid<shared->max_threads; tid++)
00617 if (parent->siblings[tid] && tid != shared->info[tree_id].thread_id)
00618 shared->stopThread(parent->siblings[tid]);
00619 }
00620 }
00621 shared->parallel_abort.inc();
00622 }
00623 SCOPED_LOCK(lk,parent->lock);
00624 if (! stopping()
00625 && (eval::betterThan(P, result, parent->search_value))) {
00626 assert(parent->window.isConsistent());
00627 parent->window.alpha(P) = result + EvalTraits<P>::delta;
00628 parent->best_move = m;
00629 parent->search_value = result;
00630 updateRootPV(P, std::cerr, result, m.move());
00631 assert(parent->window.isConsistent());
00632 shared->tree[shared->parentID(tree_id)]->pv[0] = pv[0];
00633 }
00634 }
00635 #ifndef GPSONE
00636 else if (this->multi_pv && !stopping()
00637 && eval::betterThan(P, result, w.alpha(P)))
00638 addMultiPV(P, result, m.move());
00639 #endif
00640 }
00641
00642 template <class EvalT>
00643 template <osl::Player P>
00644 void osl::search::AlphaBeta2Tree<EvalT>::
00645 examineMovesRootPar(const MoveLogProbVector& moves, size_t start, Window window,
00646 MoveLogProb& best_move, int& best_value)
00647 {
00648 const int id = shared->treeId(this);
00649 #if DEBUG_SMP > 3
00650 {
00651 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00652 std::cerr << "start split root " << id << " turn " << P << " parent " << shared->info[id].parent << "\n";
00653 history().dump();
00654 }
00655 #endif
00656 AlphaBeta2ParallelCommon::TreeInfo *info = &shared->info[id];
00657 info->window = window;
00658 info->is_root = true;
00659 info->in_pv = false;
00660 info->value = best_value;
00661 info->moves = moves;
00662 info->move_index = start;
00663 info->turn = P;
00664 info->best_move = best_move;
00665 if (! shared->split(this, id, info->thread_id, -1)) {
00666 shared->cancelled_splits.inc();
00667 throw AlphaBeta2ParallelCommon::SplitFailed();
00668 }
00669 SCOPED_LOCK(lk,info->lock);
00670 best_value = info->search_value;
00671 best_move = info->best_move;
00672 }
00673
00674 template <class EvalT>
00675 void osl::search::AlphaBeta2Tree<EvalT>::
00676 examineMovesRootPar(int tree_id)
00677 {
00678 AlphaBeta2ParallelCommon::TreeInfo *info = &shared->info[tree_id];
00679 const Player my_turn = info->turn;
00680 for (MoveLogProb m = shared->nextMove(tree_id).first;
00681 m.validMove() && ! stopping();
00682 m = shared->nextMove(tree_id).first) {
00683 #ifndef GPSONE
00684 if (this->elapsed() > 1.0)
00685 {
00686 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00687 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
00688 this->monitors())
00689 monitor->rootMove(m.move());
00690 }
00691 #endif
00692 try {
00693 if (my_turn == BLACK)
00694 testMoveRoot<BLACK>(tree_id, m);
00695 else
00696 testMoveRoot<WHITE>(tree_id, m);
00697 if (this->root_limit >= 1600)
00698 this->checkmate_searcher->runGC(this->table->isVerbose(),
00699 lastMemoryUseRatio1000());
00700 }
00701 catch (BetaCut& e) {
00702 std::cerr << "caught BetaCut at root " << info->thread_id << "\n";
00703 assert(stop_tree);
00704 break;
00705 }
00706 catch (std::runtime_error&) {
00707 stop_tree = true;
00708 this->stopNow();
00709 break;
00710 }
00711 catch (std::exception& e) {
00712 #if DEBUG_SMP > 0
00713 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00714 std::cerr << "caught " << e.what() << " at root " << info->thread_id << "\n";
00715 #endif
00716 stop_tree = true;
00717 this->stopNow();
00718 break;
00719 }
00720 catch (...) {
00721 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00722 std::cerr << "caught something at root " << info->thread_id << "\n";
00723 stop_tree = true;
00724 this->stopNow();
00725 break;
00726 }
00727 }
00728
00729 }
00730
00731 template <class EvalT>
00732 template <osl::Player P>
00733 bool osl::search::
00734 AlphaBeta2Tree<EvalT>::testMoveOther(int tree_id, const MoveLogProb& m, size_t index,
00735 bool in_pv)
00736 {
00737 if (stopping())
00738 return false;
00739
00740 Window w;
00741 AlphaBeta2ParallelCommon::TreeInfo *parent = shared->parent(tree_id);
00742 {
00743 SCOPED_LOCK(lk,parent->lock);
00744 w = parent->window;
00745 }
00746 assert(w.isConsistent() || stop_tree);
00747 if (stopping())
00748 return false;
00749 assert(P == m.player());
00750 const int result = alphaBetaSearch<P>(m, w, in_pv);
00751 if (stopping())
00752 return false;
00753
00754 bool cut = false;
00755 int parent_search_value;
00756 {
00757 #ifdef OSL_USE_RACE_DETECTOR
00758 SCOPED_LOCK(lk,parent->lock);
00759 #endif
00760 parent_search_value = parent->search_value;
00761 }
00762 if (eval::betterThan(P, result, parent_search_value)) {
00763 makePV(m.move());
00764 if (eval::betterThan(P, result, w.beta(P))) {
00765 cut = true;
00766 {
00767 boost::mutex::scoped_lock lk_smp(shared->lock_smp);
00768 SCOPED_LOCK(lk,parent->lock);
00769 if (! stop_tree) {
00770 #if DEBUG_SMP > 2
00771 std::cerr << "beta cut " << tree_id << "\n";
00772 #endif
00773 for (int tid=0; tid<shared->max_threads; tid++)
00774 if (parent->siblings[tid] && tid != shared->info[tree_id].thread_id)
00775 shared->stopThread(parent->siblings[tid]);
00776 }
00777 }
00778 shared->parallel_abort.inc();
00779 }
00780 SCOPED_LOCK(lk,parent->lock);
00781 if (! stopping() && eval::betterThan(P, result, parent->search_value)) {
00782 parent->window.alpha(P) = result + EvalTraits<P>::delta;
00783 parent->best_move = m;
00784 parent->search_value = result;
00785 parent->alpha_update++;
00786 parent->last_alpha_update = index;
00787 assert(cut || shared->tree[shared->info[tree_id].parent]->stop_tree
00788 || parent->window.isConsistent());
00789 AlphaBeta2Tree *pp = shared->tree[shared->parentID(tree_id)];
00790 pp->pv[pp->curDepth()] = pv[curDepth()];
00791 if (cut)
00792 return true;
00793 }
00794 }
00795 return false;
00796 }
00797
00798 template <class EvalT>
00799 template <osl::Player P>
00800 bool osl::search::AlphaBeta2Tree<EvalT>::
00801 examineMovesOther(Window& w, MoveLogProb& best_move, int& best_value,
00802 int& tried_moves, int& alpha_update, int& last_alpha_update)
00803 {
00804 assert(w.isConsistent());
00805
00806 const int id = shared->treeId(this);
00807 #if DEBUG_SMP > 3
00808 {
00809 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00810 std::cerr << "start split at " << curLimit() << " " << id << " turn " << P
00811 << " move " << tried_moves
00812 << " parent " << shared->info[id].parent << "\n";
00813 history().dump();
00814 }
00815 #endif
00816 AlphaBeta2ParallelCommon::TreeInfo *info = &shared->info[id];
00817 info->window = w;
00818 info->is_root = false;
00819 info->in_pv = (! w.null()) && (! best_move.validMove());
00820 info->value = best_value;
00821 info->move_index = tried_moves;
00822 info->turn = P;
00823 info->best_move = best_move;
00824 info->alpha_update = alpha_update;
00825 info->last_alpha_update = last_alpha_update;
00826 if (! shared->split(this, id, info->thread_id, shared->max_thread_group)) {
00827 #if DEBUG_SMP > 2
00828 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00829 std::cerr << "failed split " << id << " turn " << P << "\n";
00830 for (int i=0; i<shared->max_threads; ++i) {
00831 std::cerr << " " << i << " " << shared->job[i] << "\n";
00832 }
00833 #endif
00834 shared->cancelled_splits.inc();
00835 throw AlphaBeta2ParallelCommon::SplitFailed();
00836 }
00837 SCOPED_LOCK(lk,info->lock);
00838 best_value = info->search_value;
00839 best_move = info->best_move;
00840 w = info->window;
00841 tried_moves = info->move_index;
00842 alpha_update += info->alpha_update;
00843 last_alpha_update = info->last_alpha_update;
00844 #if DEBUG_SMP > 3
00845 {
00846 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00847 std::cerr << "back from split at " << curLimit() << " " << id << " turn " << P << " parent " << shared->info[id].parent << "\n";
00848 }
00849 #endif
00850 testStop();
00851 return EvalTraits<P>::betterThan(best_value, w.beta(P));
00852 }
00853
00854 template <class EvalT>
00855 void osl::search::AlphaBeta2Tree<EvalT>::
00856 examineMovesOther(int tree_id)
00857 {
00858 AlphaBeta2ParallelCommon::TreeInfo *parent = shared->parent(tree_id);
00859 for (std::pair<MoveLogProb,size_t> m = shared->nextMove(tree_id); m.first.validMove() && !stopping();
00860 m = shared->nextMove(tree_id)) {
00861 bool in_pv = parent->in_pv;
00862 if (in_pv) {
00863 in_pv = ! parent->best_move.validMove();
00864 }
00865 assert(parent->turn == m.first.player());
00866 try {
00867 const bool cut_by_move =
00868 (parent->turn == BLACK)
00869 ? testMoveOther<BLACK>(tree_id, m.first, m.second, in_pv)
00870 : testMoveOther<WHITE>(tree_id, m.first, m.second, in_pv);
00871 if (cut_by_move) {
00872 break;
00873 }
00874 testStop();
00875 }
00876 catch (BetaCut&) {
00877 assert(stop_tree);
00878 }
00879 catch (TableFull&) {
00880 stop_tree = true;
00881 this->stopNow();
00882 break;
00883 }
00884 catch (misc::NoMoreTime&) {
00885 stop_tree = true;
00886 this->stopNow();
00887 #if DEBUG_SMP > 2
00888 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00889 std::cerr << "caught timeout in tree " << tree_id << " thread " << shared->info[tree_id].thread_id << "\n";
00890 #endif
00891 break;
00892 }
00893 catch (NoMoreMemory&) {
00894 stop_tree = true;
00895 this->stopNow();
00896 #if DEBUG_SMP > 2
00897 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00898 std::cerr << "caught memory full in tree " << tree_id << " thread " << shared->info[tree_id].thread_id << "\n";
00899 #endif
00900 break;
00901 }
00902 catch (std::exception& e) {
00903 this->stopNow();
00904 stop_tree = true;
00905 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00906 std::cerr << "caught exception at " << tree_id << " " << e.what() << "\n";
00907 break;
00908 }
00909 catch (...) {
00910 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00911 std::cerr << "caught unknown exception at " << tree_id << "\n";
00912 throw;
00913 }
00914 }
00915
00916 }
00917
00918 namespace osl
00919 {
00920 namespace search
00921 {
00922 #ifndef MINIMAL
00923 template struct AlphaBeta2Parallel<eval::ProgressEval>;
00924
00925 template
00926 bool AlphaBeta2Tree<eval::ProgressEval>::examineMovesOther<BLACK>(Window&, MoveLogProb&, int&, int&, int&, int&);
00927 template
00928 bool AlphaBeta2Tree<eval::ProgressEval>::examineMovesOther<WHITE>(Window&, MoveLogProb&, int&, int&, int&, int&);
00929
00930 template
00931 void AlphaBeta2Tree<eval::ProgressEval>::examineMovesRootPar<BLACK>(const MoveLogProbVector&, size_t, Window, MoveLogProb&, int&);
00932 template
00933 void AlphaBeta2Tree<eval::ProgressEval>::examineMovesRootPar<WHITE>(const MoveLogProbVector&, size_t, Window, MoveLogProb&, int&);
00934 #endif
00935 template struct AlphaBeta2Parallel<eval::ml::OpenMidEndingEval>;
00936
00937 template
00938 bool AlphaBeta2Tree<eval::ml::OpenMidEndingEval>::examineMovesOther<BLACK>(Window&, MoveLogProb&, int&, int&, int&, int&);
00939 template
00940 bool AlphaBeta2Tree<eval::ml::OpenMidEndingEval>::examineMovesOther<WHITE>(Window&, MoveLogProb&, int&, int&, int&, int&);
00941
00942 template
00943 void AlphaBeta2Tree<eval::ml::OpenMidEndingEval>::examineMovesRootPar<BLACK>(const MoveLogProbVector&, size_t, Window, MoveLogProb&, int&);
00944 template
00945 void AlphaBeta2Tree<eval::ml::OpenMidEndingEval>::examineMovesRootPar<WHITE>(const MoveLogProbVector&, size_t, Window, MoveLogProb&, int&);
00946 }
00947 }
00948
00949 #endif
00950
00951
00952
00953
00954