alphaBeta2Parallel.cc
Go to the documentation of this file.
00001 /* alphaBeta2Parallel.cc
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 // #define OSL_BUSY_WAIT
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       // first, assgin job to splitting thread
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)         // he is working
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   // cut or no more moves to search
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   // cut or no more moves to search
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 /* OSL_SMP */
00950 /* ------------------------------------------------------------------------- */
00951 // ;;; Local Variables:
00952 // ;;; mode:c++
00953 // ;;; c-basic-offset:2
00954 // ;;; End:
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines