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 size_t result = master->checkmateSearcher().mainNodeCount();
00574 #ifndef NEW_DFPN
00575 for (int i=1; i<max_threads; ++i)
00576 result += checkmate[i]->mainNodeCount();
00577 #endif
00578 return result;
00579 }
00580
00581
00582
00583 template <class EvalT>
00584 template <osl::Player P>
00585 void osl::search::
00586 AlphaBeta2Tree<EvalT>::testMoveRoot(int tree_id, const MoveLogProb& m)
00587 {
00588 if (stop_tree) {
00589 std::cerr << "root tree stop\n";
00590 return;
00591 }
00592
00593 Window w;
00594 AlphaBeta2ParallelCommon::TreeInfo *parent = shared->parent(tree_id);
00595 {
00596 SCOPED_LOCK(lk,parent->lock);
00597 w = parent->window;
00598 assert(w.isConsistent());
00599 }
00600 assert(P == m.player());
00601 #ifndef GPSONE
00602 if (this->multi_pv) {
00603 int width = this->multi_pv*this->eval.captureValue(newPtypeO(P, PAWN))/200;
00604 if (width % 2 == 0)
00605 width -= EvalTraits<P>::delta;
00606 w.alpha(P) = parent->search_value + width;
00607 }
00608 #endif
00609 const int result = alphaBetaSearch<P>(m, w, false);
00610
00611 if (eval::betterThan(P, result, parent->search_value)) {
00612 makePV(m.move());
00613 if (eval::betterThan(P, result, w.beta(P))) {
00614 {
00615 boost::mutex::scoped_lock lk_smp(shared->lock_smp);
00616 SCOPED_LOCK(lk,parent->lock);
00617 if (! stop_tree) {
00618 #if DEBUG_SMP > 2
00619 std::cerr << "beta cut root " << tree_id << "\n";
00620 #endif
00621 for (int tid=0; tid<shared->max_threads; tid++)
00622 if (parent->siblings[tid] && tid != shared->info[tree_id].thread_id)
00623 shared->stopThread(parent->siblings[tid]);
00624 }
00625 }
00626 shared->parallel_abort.inc();
00627 }
00628 SCOPED_LOCK(lk,parent->lock);
00629 if (! stopping()
00630 && (eval::betterThan(P, result, parent->search_value))) {
00631 assert(parent->window.isConsistent());
00632 parent->window.alpha(P) = result + EvalTraits<P>::delta;
00633 parent->best_move = m;
00634 parent->search_value = result;
00635 updateRootPV(P, std::cerr, result, m.move());
00636 assert(parent->window.isConsistent());
00637 shared->tree[shared->parentID(tree_id)]->pv[0] = pv[0];
00638 }
00639 }
00640 #ifndef GPSONE
00641 else if (this->multi_pv && !stopping()
00642 && eval::betterThan(P, result, w.alpha(P)))
00643 addMultiPV(P, result, m.move());
00644 #endif
00645 }
00646
00647 template <class EvalT>
00648 template <osl::Player P>
00649 void osl::search::AlphaBeta2Tree<EvalT>::
00650 examineMovesRootPar(const MoveLogProbVector& moves, size_t start, Window window,
00651 MoveLogProb& best_move, int& best_value)
00652 {
00653 const int id = shared->treeId(this);
00654 #if DEBUG_SMP > 3
00655 {
00656 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00657 std::cerr << "start split root " << id << " turn " << P << " parent " << shared->info[id].parent << "\n";
00658 history().dump();
00659 }
00660 #endif
00661 AlphaBeta2ParallelCommon::TreeInfo *info = &shared->info[id];
00662 info->window = window;
00663 info->is_root = true;
00664 info->in_pv = false;
00665 info->value = best_value;
00666 info->moves = moves;
00667 info->move_index = start;
00668 info->turn = P;
00669 info->best_move = best_move;
00670 if (! shared->split(this, id, info->thread_id, -1)) {
00671 shared->cancelled_splits.inc();
00672 throw AlphaBeta2ParallelCommon::SplitFailed();
00673 }
00674 SCOPED_LOCK(lk,info->lock);
00675 best_value = info->search_value;
00676 best_move = info->best_move;
00677 }
00678
00679 template <class EvalT>
00680 void osl::search::AlphaBeta2Tree<EvalT>::
00681 examineMovesRootPar(int tree_id)
00682 {
00683 AlphaBeta2ParallelCommon::TreeInfo *info = &shared->info[tree_id];
00684 const Player my_turn = info->turn;
00685 for (MoveLogProb m = shared->nextMove(tree_id).first;
00686 m.validMove() && ! stopping();
00687 m = shared->nextMove(tree_id).first) {
00688 #ifndef GPSONE
00689 if (this->elapsed() > 1.0)
00690 {
00691 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00692 BOOST_FOREACH(const boost::shared_ptr<SearchMonitor>& monitor,
00693 this->monitors())
00694 monitor->rootMove(m.move());
00695 }
00696 #endif
00697 try {
00698 if (my_turn == BLACK)
00699 testMoveRoot<BLACK>(tree_id, m);
00700 else
00701 testMoveRoot<WHITE>(tree_id, m);
00702 if (this->root_limit >= 1600)
00703 this->checkmate_searcher->runGC(this->table->isVerbose(),
00704 lastMemoryUseRatio1000());
00705 }
00706 catch (BetaCut& e) {
00707 std::cerr << "caught BetaCut at root " << info->thread_id << "\n";
00708 assert(stop_tree);
00709 break;
00710 }
00711 catch (std::runtime_error&) {
00712 stop_tree = true;
00713 this->stopNow();
00714 break;
00715 }
00716 catch (std::exception& e) {
00717 #if DEBUG_SMP > 0
00718 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00719 std::cerr << "caught " << e.what() << " at root " << info->thread_id << "\n";
00720 #endif
00721 stop_tree = true;
00722 this->stopNow();
00723 break;
00724 }
00725 catch (...) {
00726 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00727 std::cerr << "caught something at root " << info->thread_id << "\n";
00728 stop_tree = true;
00729 this->stopNow();
00730 break;
00731 }
00732 }
00733
00734 }
00735
00736 template <class EvalT>
00737 template <osl::Player P>
00738 bool osl::search::
00739 AlphaBeta2Tree<EvalT>::testMoveOther(int tree_id, const MoveLogProb& m, size_t index,
00740 bool in_pv)
00741 {
00742 if (stopping())
00743 return false;
00744
00745 Window w;
00746 AlphaBeta2ParallelCommon::TreeInfo *parent = shared->parent(tree_id);
00747 {
00748 SCOPED_LOCK(lk,parent->lock);
00749 w = parent->window;
00750 assert(w.isConsistent() || stop_tree);
00751 }
00752 if (stopping())
00753 return false;
00754 assert(P == m.player());
00755 const int result = alphaBetaSearch<P>(m, w, in_pv);
00756 if (stopping())
00757 return false;
00758
00759 bool cut = false;
00760 int parent_search_value;
00761 {
00762 #ifdef OSL_USE_RACE_DETECTOR
00763 SCOPED_LOCK(lk,parent->lock);
00764 #endif
00765 parent_search_value = parent->search_value;
00766 }
00767 if (eval::betterThan(P, result, parent_search_value)) {
00768 makePV(m.move());
00769 if (eval::betterThan(P, result, w.beta(P))) {
00770 cut = true;
00771 {
00772 boost::mutex::scoped_lock lk_smp(shared->lock_smp);
00773 SCOPED_LOCK(lk,parent->lock);
00774 if (! stop_tree) {
00775 #if DEBUG_SMP > 2
00776 std::cerr << "beta cut " << tree_id << "\n";
00777 #endif
00778 for (int tid=0; tid<shared->max_threads; tid++)
00779 if (parent->siblings[tid] && tid != shared->info[tree_id].thread_id)
00780 shared->stopThread(parent->siblings[tid]);
00781 }
00782 }
00783 shared->parallel_abort.inc();
00784 }
00785 SCOPED_LOCK(lk,parent->lock);
00786 if (! stopping() && eval::betterThan(P, result, parent->search_value)) {
00787 parent->window.alpha(P) = result + EvalTraits<P>::delta;
00788 parent->best_move = m;
00789 parent->search_value = result;
00790 parent->alpha_update++;
00791 parent->last_alpha_update = index;
00792 assert(cut || shared->tree[shared->info[tree_id].parent]->stop_tree
00793 || parent->window.isConsistent());
00794 AlphaBeta2Tree *pp = shared->tree[shared->parentID(tree_id)];
00795 pp->pv[pp->curDepth()] = pv[curDepth()];
00796 if (cut)
00797 return true;
00798 }
00799 }
00800 return false;
00801 }
00802
00803 template <class EvalT>
00804 template <osl::Player P>
00805 bool osl::search::AlphaBeta2Tree<EvalT>::
00806 examineMovesOther(Window& w, MoveLogProb& best_move, int& best_value,
00807 int& tried_moves, int& alpha_update, int& last_alpha_update)
00808 {
00809 assert(w.isConsistent());
00810
00811 const int id = shared->treeId(this);
00812 #if DEBUG_SMP > 3
00813 {
00814 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00815 std::cerr << "start split at " << curLimit() << " " << id << " turn " << P
00816 << " move " << tried_moves
00817 << " parent " << shared->info[id].parent << "\n";
00818 history().dump();
00819 }
00820 #endif
00821 AlphaBeta2ParallelCommon::TreeInfo *info = &shared->info[id];
00822 info->window = w;
00823 info->is_root = false;
00824 info->in_pv = (! w.null()) && (! best_move.validMove());
00825 info->value = best_value;
00826 info->move_index = tried_moves;
00827 info->turn = P;
00828 info->best_move = best_move;
00829 info->alpha_update = alpha_update;
00830 info->last_alpha_update = last_alpha_update;
00831 if (! shared->split(this, id, info->thread_id, shared->max_thread_group)) {
00832 #if DEBUG_SMP > 2
00833 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00834 std::cerr << "failed split " << id << " turn " << P << "\n";
00835 for (int i=0; i<shared->max_threads; ++i) {
00836 std::cerr << " " << i << " " << shared->job[i] << "\n";
00837 }
00838 #endif
00839 shared->cancelled_splits.inc();
00840 throw AlphaBeta2ParallelCommon::SplitFailed();
00841 }
00842 SCOPED_LOCK(lk,info->lock);
00843 best_value = info->search_value;
00844 best_move = info->best_move;
00845 w = info->window;
00846 tried_moves = info->move_index;
00847 alpha_update += info->alpha_update;
00848 last_alpha_update = info->last_alpha_update;
00849 #if DEBUG_SMP > 3
00850 {
00851 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00852 std::cerr << "back from split at " << curLimit() << " " << id << " turn " << P << " parent " << shared->info[id].parent << "\n";
00853 }
00854 #endif
00855 testStop();
00856 return EvalTraits<P>::betterThan(best_value, w.beta(P));
00857 }
00858
00859 template <class EvalT>
00860 void osl::search::AlphaBeta2Tree<EvalT>::
00861 examineMovesOther(int tree_id)
00862 {
00863 AlphaBeta2ParallelCommon::TreeInfo *parent = shared->parent(tree_id);
00864 for (std::pair<MoveLogProb,size_t> m = shared->nextMove(tree_id); m.first.validMove() && !stopping();
00865 m = shared->nextMove(tree_id)) {
00866 bool in_pv = parent->in_pv;
00867 if (in_pv) {
00868 in_pv = ! parent->best_move.validMove();
00869 }
00870 assert(parent->turn == m.first.player());
00871 try {
00872 const bool cut_by_move =
00873 (parent->turn == BLACK)
00874 ? testMoveOther<BLACK>(tree_id, m.first, m.second, in_pv)
00875 : testMoveOther<WHITE>(tree_id, m.first, m.second, in_pv);
00876 if (cut_by_move) {
00877 break;
00878 }
00879 testStop();
00880 }
00881 catch (BetaCut&) {
00882 assert(stop_tree);
00883 }
00884 catch (TableFull&) {
00885 stop_tree = true;
00886 this->stopNow();
00887 break;
00888 }
00889 catch (misc::NoMoreTime&) {
00890 stop_tree = true;
00891 this->stopNow();
00892 #if DEBUG_SMP > 2
00893 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00894 std::cerr << "caught timeout in tree " << tree_id << " thread " << shared->info[tree_id].thread_id << "\n";
00895 #endif
00896 break;
00897 }
00898 catch (NoMoreMemory&) {
00899 stop_tree = true;
00900 this->stopNow();
00901 #if DEBUG_SMP > 2
00902 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00903 std::cerr << "caught memory full in tree " << tree_id << " thread " << shared->info[tree_id].thread_id << "\n";
00904 #endif
00905 break;
00906 }
00907 catch (std::exception& e) {
00908 this->stopNow();
00909 stop_tree = true;
00910 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00911 std::cerr << "caught exception at " << tree_id << " " << e.what() << "\n";
00912 break;
00913 }
00914 catch (...) {
00915 boost::mutex::scoped_lock lk(OslConfig::lock_io);
00916 std::cerr << "caught unknown exception at " << tree_id << "\n";
00917 throw;
00918 }
00919 }
00920
00921 }
00922
00923 namespace osl
00924 {
00925 namespace search
00926 {
00927 #ifndef MINIMAL
00928 template struct AlphaBeta2Parallel<eval::ProgressEval>;
00929
00930 template
00931 bool AlphaBeta2Tree<eval::ProgressEval>::examineMovesOther<BLACK>(Window&, MoveLogProb&, int&, int&, int&, int&);
00932 template
00933 bool AlphaBeta2Tree<eval::ProgressEval>::examineMovesOther<WHITE>(Window&, MoveLogProb&, int&, int&, int&, int&);
00934
00935 template
00936 void AlphaBeta2Tree<eval::ProgressEval>::examineMovesRootPar<BLACK>(const MoveLogProbVector&, size_t, Window, MoveLogProb&, int&);
00937 template
00938 void AlphaBeta2Tree<eval::ProgressEval>::examineMovesRootPar<WHITE>(const MoveLogProbVector&, size_t, Window, MoveLogProb&, int&);
00939 #endif
00940 template struct AlphaBeta2Parallel<eval::ml::OpenMidEndingEval>;
00941
00942 template
00943 bool AlphaBeta2Tree<eval::ml::OpenMidEndingEval>::examineMovesOther<BLACK>(Window&, MoveLogProb&, int&, int&, int&, int&);
00944 template
00945 bool AlphaBeta2Tree<eval::ml::OpenMidEndingEval>::examineMovesOther<WHITE>(Window&, MoveLogProb&, int&, int&, int&, int&);
00946
00947 template
00948 void AlphaBeta2Tree<eval::ml::OpenMidEndingEval>::examineMovesRootPar<BLACK>(const MoveLogProbVector&, size_t, Window, MoveLogProb&, int&);
00949 template
00950 void AlphaBeta2Tree<eval::ml::OpenMidEndingEval>::examineMovesRootPar<WHITE>(const MoveLogProbVector&, size_t, Window, MoveLogProb&, int&);
00951 }
00952 }
00953
00954 #endif
00955
00956
00957
00958
00959