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