00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #ifndef __WORKLIST_H_
00021 #define __WORKLIST_H_
00022
00023 #include <queue>
00024 #include <stack>
00025 #include <limits>
00026 #include <map>
00027 #include <boost/utility.hpp>
00028
00029 #include "Galois/Runtime/PaddedLock.h"
00030 #include "Galois/Runtime/PerCPU.h"
00031
00032
00033 #include <boost/utility.hpp>
00034
00035 #include "mm/mem.h"
00036 #include "WorkListHelpers.h"
00037
00038
00039 #define OPTNOINLINE
00040
00041 #ifndef WLCOMPILECHECK
00042 #define WLCOMPILECHECK(name) //
00043 #endif
00044
00045 namespace GaloisRuntime {
00046 namespace WorkList {
00047
00048
00049
00050
00051 template<typename T, bool concurrent>
00052 class AbstractWorkList {
00053 public:
00055 typedef T value_type;
00056
00058 template<bool newconcurrent>
00059 struct rethread {
00060 typedef AbstractWorkList<T, newconcurrent> WL;
00061 };
00062
00064 template<typename Tnew>
00065 struct retype {
00066 typedef AbstractWorkList<Tnew, concurrent> WL;
00067 };
00068
00070 bool push(value_type val);
00072 bool aborted(value_type val);
00074 std::pair<bool, value_type> pop();
00077 bool empty() const;
00078
00080 template<typename iter>
00081 void fill_initial(iter begin, iter end);
00082
00083
00084 };
00085
00089
00090
00091 template<class Compare = std::less<int>, typename T = int, bool concurrent = true>
00092 class PriQueue : private boost::noncopyable, private PaddedLock<concurrent> {
00093
00094 std::priority_queue<T, std::vector<T>, Compare> wl;
00095
00096 using PaddedLock<concurrent>::lock;
00097 using PaddedLock<concurrent>::try_lock;
00098 using PaddedLock<concurrent>::unlock;
00099
00100 public:
00101 template<bool newconcurrent>
00102 struct rethread {
00103 typedef PriQueue<Compare, T, newconcurrent> WL;
00104 };
00105 template<typename Tnew>
00106 struct retype {
00107 typedef PriQueue<Compare, Tnew, concurrent> WL;
00108 };
00109
00110 typedef T value_type;
00111
00112 bool push(value_type val) {
00113 lock();
00114 wl.push(val);
00115 unlock();
00116 return true;
00117 }
00118
00119 std::pair<bool, value_type> pop() {
00120 lock();
00121 if (wl.empty()) {
00122 unlock();
00123 return std::make_pair(false, value_type());
00124 } else {
00125 value_type retval = wl.top();
00126 wl.pop();
00127 unlock();
00128 return std::make_pair(true, retval);
00129 }
00130 }
00131
00132 bool empty() const {
00133 return wl.empty();
00134 }
00135
00136 bool aborted(value_type val) {
00137 return push(val);
00138 }
00139
00140
00141 template<typename Iter>
00142 void fill_initial(Iter ii, Iter ee) {
00143 while (ii != ee) {
00144 wl.push(*ii++);
00145 }
00146 }
00147 };
00148 WLCOMPILECHECK(PriQueue);
00149
00150 template<typename T = int, bool concurrent = true>
00151 class LIFO : private boost::noncopyable, private PaddedLock<concurrent> {
00152 std::deque<T> wl;
00153
00154 using PaddedLock<concurrent>::lock;
00155 using PaddedLock<concurrent>::try_lock;
00156 using PaddedLock<concurrent>::unlock;
00157
00158 public:
00159 template<bool newconcurrent>
00160 struct rethread {
00161 typedef LIFO<T, newconcurrent> WL;
00162 };
00163 template<typename Tnew>
00164 struct retype {
00165 typedef LIFO<Tnew, concurrent> WL;
00166 };
00167
00168 typedef T value_type;
00169
00170 bool push(value_type val) OPTNOINLINE {
00171 lock();
00172 wl.push_back(val);
00173 unlock();
00174 return true;
00175 }
00176
00177 std::pair<bool, value_type> pop() OPTNOINLINE {
00178 lock();
00179 if (wl.empty()) {
00180 unlock();
00181 return std::make_pair(false, value_type());
00182 } else {
00183 value_type retval = wl.back();
00184 wl.pop_back();
00185 unlock();
00186 return std::make_pair(true, retval);
00187 }
00188 }
00189
00190 bool empty() const OPTNOINLINE {
00191 return wl.empty();
00192 }
00193
00194 bool aborted(value_type val) {
00195 return push(val);
00196 }
00197
00198
00199 template<typename Iter>
00200 void fill_initial(Iter ii, Iter ee) {
00201 wl.insert(wl.end(), ii, ee);
00202 }
00203 };
00204 WLCOMPILECHECK(LIFO);
00205
00206 template<typename T = int, bool concurrent = true>
00207 class FIFO : private boost::noncopyable, private PaddedLock<concurrent> {
00208 std::deque<T> wl;
00209
00210 using PaddedLock<concurrent>::lock;
00211 using PaddedLock<concurrent>::try_lock;
00212 using PaddedLock<concurrent>::unlock;
00213
00214 public:
00215 template<bool newconcurrent>
00216 struct rethread {
00217 typedef FIFO<T, newconcurrent> WL;
00218 };
00219 template<typename Tnew>
00220 struct retype {
00221 typedef FIFO<Tnew, concurrent> WL;
00222 };
00223
00224 typedef T value_type;
00225
00226 bool push(value_type val) {
00227 lock();
00228 wl.push_back(val);
00229 unlock();
00230 return true;
00231 }
00232
00233 std::pair<bool, value_type> pop() {
00234 lock();
00235 if (wl.empty()) {
00236 unlock();
00237 return std::make_pair(false, value_type());
00238 } else {
00239 value_type retval = wl.front();
00240 wl.pop_front();
00241 unlock();
00242 return std::make_pair(true, retval);
00243 }
00244 }
00245
00246 bool empty() const {
00247 return wl.empty();
00248 }
00249
00250 bool aborted(value_type val) {
00251 return push(val);
00252 }
00253
00254
00255 template<typename Iter>
00256 void fill_initial(Iter ii, Iter ee) {
00257 wl.insert(wl.end(), ii, ee);
00258 }
00259 };
00260 WLCOMPILECHECK(FIFO);
00261
00262 template<class Indexer = DummyIndexer, typename ContainerTy = FIFO<>, typename T = int, bool concurrent = true >
00263 class OrderedByIntegerMetric : private boost::noncopyable {
00264
00265 typedef typename ContainerTy::template rethread<concurrent>::WL CTy;
00266
00267 struct perItem {
00268 CTy* current;
00269 int lastMasterVersion;
00270 std::map<int, CTy*> local;
00271 };
00272
00273 std::vector<std::pair<int, CTy*> > masterLog;
00274 PaddedLock<concurrent> masterLock;
00275 int masterVersion;
00276
00277 Indexer I;
00278
00279 PerCPU<perItem> current;
00280
00281 void updateLocal_i(perItem& p) {
00282
00283 for (; p.lastMasterVersion < masterVersion; ++p.lastMasterVersion) {
00284 std::pair<int, CTy*> logEntry = masterLog[p.lastMasterVersion];
00285 p.local[logEntry.first] = logEntry.second;
00286 }
00287 }
00288
00289 void updateLocal(perItem& p) {
00290 if (p.lastMasterVersion != masterVersion) {
00291 masterLock.lock();
00292 updateLocal_i(p);
00293 masterLock.unlock();
00294 }
00295 }
00296
00297 CTy* updateLocalOrCreate(perItem& p, int i) {
00298
00299 CTy*& lC = p.local[i];
00300 if (lC)
00301 return lC;
00302 masterLock.lock();
00303 updateLocal_i(p);
00304 if (!lC) {
00305 lC = new CTy();
00306 ++masterVersion;
00307 masterLog.push_back(std::make_pair(i, lC));
00308 }
00309 masterLock.unlock();
00310 return lC;
00311 }
00312
00313 public:
00314 template<bool newconcurrent>
00315 struct rethread {
00316 typedef OrderedByIntegerMetric<Indexer,ContainerTy,T,newconcurrent> WL;
00317 };
00318 template<typename Tnew>
00319 struct retype {
00320 typedef OrderedByIntegerMetric<Indexer,typename ContainerTy::template retype<Tnew>::WL,Tnew,concurrent> WL;
00321 };
00322
00323 typedef T value_type;
00324
00325 OrderedByIntegerMetric(const Indexer& x = Indexer())
00326 :masterVersion(0), I(x)
00327 {
00328 for (unsigned int i = 0; i < current.size(); ++i) {
00329 current.get(i).current = 0;
00330 current.get(i).lastMasterVersion = 0;
00331 }
00332 }
00333
00334 bool push(value_type val) {
00335 unsigned int index = I(val);
00336 perItem& pI = current.get();
00337 CTy* lC = updateLocalOrCreate(pI, index);
00338 return lC->push(val);
00339 }
00340
00341 std::pair<bool, value_type> pop() {
00342
00343 perItem& pI = current.get();
00344 CTy*& C = pI.current;
00345 std::pair<bool, value_type> retval;
00346 if (C && (retval = C->pop()).first)
00347 return retval;
00348
00349 updateLocal(pI);
00350 for (typename std::map<int, CTy*>::iterator ii = pI.local.begin(), ee = pI.local.end(); ii != ee; ++ii) {
00351 C = ii->second;
00352 if ((retval = C->pop()).first)
00353 return retval;
00354 }
00355 retval.first = false;
00356 return retval;
00357 }
00358
00359 bool empty() const {
00360 for (typename std::vector<std::pair<int, CTy*> >::const_iterator ii = masterLog.begin(), ee = masterLog.end(); ii != ee; ++ii)
00361 if (!ii->second->empty())
00362 return false;
00363 return true;
00364 }
00365
00366 bool aborted(value_type val) {
00367 return push(val);
00368 }
00369
00370
00371 template<typename Iter>
00372 void fill_initial(Iter ii, Iter ee) {
00373 while (ii != ee) {
00374 push(*ii++);
00375 }
00376 }
00377 };
00378 WLCOMPILECHECK(OrderedByIntegerMetric);
00379
00380 template<typename GlobalQueueTy = FIFO<>, typename LocalQueueTy = FIFO<>, typename T = int >
00381 class LocalQueues {
00382
00383 PerCPU<typename LocalQueueTy::template rethread<false>::WL> local;
00384 GlobalQueueTy global;
00385
00386 public:
00387 template<bool newconcurrent>
00388 struct rethread {
00389 typedef LocalQueues<GlobalQueueTy, LocalQueueTy, T> WL;
00390 };
00391 template<typename Tnew>
00392 struct retype {
00393 typedef LocalQueues<typename GlobalQueueTy::template retype<Tnew>::WL, typename LocalQueueTy::template retype<Tnew>::WL, Tnew> WL;
00394 };
00395
00396 typedef T value_type;
00397
00398 LocalQueues() {}
00399
00400 bool push(value_type val) {
00401 return local.get().push(val);
00402 }
00403
00404 bool aborted(value_type val) {
00405
00406 return global.push(val);
00407 }
00408
00409 std::pair<bool, value_type> pop() {
00410 std::pair<bool, value_type> ret = local.get().pop();
00411 if (ret.first)
00412 return ret;
00413 ret = global.pop();
00414 return ret;
00415 }
00416
00417 bool empty() {
00418 if (!local.get().empty()) return false;
00419 return global.empty();
00420 }
00421
00422 template<typename iter>
00423 void fill_initial(iter begin, iter end) {
00424 global.fill_initial(begin,end);
00425 }
00426 };
00427 WLCOMPILECHECK(LocalQueues);
00428
00429
00430 template<typename T = int>
00431 class MP_SC_FIFO {
00432 class Chunk : public FixedSizeRing<T, 128, false>, public ConExtLinkedQueue<Chunk,true>::ListNode { };
00433
00434 MM::FixedSizeAllocator heap;
00435
00436 struct p {
00437 PtrLock<Chunk*, true> next;
00438 };
00439
00440 PerCPU<p> data;
00441 ConExtLinkedQueue<Chunk, true> queue;
00442 Chunk* current;
00443
00444 Chunk* mkChunk() {
00445 return new (heap.allocate(sizeof(Chunk))) Chunk();
00446 }
00447
00448 void delChunk(Chunk* C) {
00449 C->~Chunk();
00450 heap.deallocate(C);
00451 }
00452
00453 public:
00454 typedef T value_type;
00455
00456 MP_SC_FIFO() :heap(sizeof(Chunk)), current(0) {}
00457
00458 template<bool newconcurrent>
00459 struct rethread {
00460 typedef MP_SC_FIFO<T> WL;
00461 };
00462 template<typename Tnew>
00463 struct retype {
00464 typedef MP_SC_FIFO<Tnew> WL;
00465 };
00466
00467 bool push(value_type val) {
00468 p& n = data.get();
00469 n.next.lock();
00470 if (n.next.getValue() && n.next.getValue()->push_back(val)){
00471 n.next.unlock();
00472 return true;
00473 }
00474 if (n.next.getValue())
00475 queue.push(n.next.getValue());
00476 Chunk* C = mkChunk();
00477 bool worked = C->push_back(val);
00478 assert(worked);
00479 n.next.unlock_and_set(C);
00480 return true;
00481 }
00482
00483 bool aborted(value_type val) {
00484 return push(val);
00485 }
00486
00487 std::pair<bool, value_type> pop() {
00488 #define ACT if (current && (ret = current->pop_front()).first) return ret; if (current) delChunk(current);
00489
00490 std::pair<bool, value_type> ret;
00491 ACT;
00492
00493 current = queue.pop();
00494 ACT;
00495
00496 current = data.get().next.getValue();
00497 data.get().next.setValue(0);
00498 ACT;
00499
00500 for (unsigned int i = 0; i < data.size(); ++i) {
00501 p& n = data.get(i);
00502 if (n.next.getValue()) {
00503 n.next.lock();
00504 current = n.next.getValue();
00505 n.next.unlock_and_set(0);
00506 ACT;
00507 }
00508 }
00509 current = 0;
00510
00511 return std::make_pair(false, value_type());
00512 #undef ACT
00513 }
00514
00515 bool empty() {
00516 for (unsigned int i = 0; i < data.size(); ++i) {
00517 p& n = data.get(i);
00518 if (n.next.getValue() && !n.next.getValue()->empty())
00519 return false;
00520 }
00521 return queue.empty();
00522 }
00523
00524
00526 template<typename iter>
00527 void fill_initial(iter begin, iter end) {
00528 while (begin != end)
00529 push(*begin++);
00530 }
00531
00532 };
00533 WLCOMPILECHECK(MP_SC_FIFO);
00534
00535
00536 template<bool d, typename TQ>
00537 struct squeues;
00538
00539 template<typename TQ>
00540 struct squeues<true,TQ> {
00541 PerLevel<TQ> queues;
00542 TQ& get(int i) { return queues.get(i); }
00543 TQ& get() { return queues.get(); }
00544 int myEffectiveID() { return queues.myEffectiveID(); }
00545 int size() { return queues.size(); }
00546 };
00547
00548 template<typename TQ>
00549 struct squeues<false,TQ> {
00550 TQ queue;
00551 TQ& get(int i) { return queue; }
00552 TQ& get() { return queue; }
00553 int myEffectiveID() { return 0; }
00554 int size() { return 0; }
00555 };
00556
00557 template<typename T, template<typename, bool> class QT, bool distributed = false, bool isStack = false, int chunksize=64, bool concurrent=true>
00558 class ChunkedMaster : private boost::noncopyable {
00559 class Chunk : public FixedSizeRing<T, chunksize, false>, public QT<Chunk, concurrent>::ListNode {};
00560
00561 MM::FixedSizeAllocator heap;
00562
00563 struct p {
00564 Chunk* cur;
00565 Chunk* next;
00566 };
00567
00568 typedef QT<Chunk, concurrent> LevelItem;
00569
00570 PerCPU<p> data;
00571 squeues<distributed, LevelItem> Q;
00572
00573 Chunk* mkChunk() {
00574 return new (heap.allocate(sizeof(Chunk))) Chunk();
00575 }
00576
00577 void delChunk(Chunk* C) {
00578 C->~Chunk();
00579 heap.deallocate(C);
00580 }
00581
00582 void pushChunk(Chunk* C) OPTNOINLINE {
00583 LevelItem& I = Q.get();
00584 I.push(C);
00585 }
00586
00587 Chunk* popChunkByID(unsigned int i) OPTNOINLINE {
00588 LevelItem& I = Q.get(i);
00589 return I.pop();
00590 }
00591
00592 Chunk* popChunk() OPTNOINLINE {
00593 int id = Q.myEffectiveID();
00594 Chunk* r = popChunkByID(id);
00595 if (r)
00596 return r;
00597
00598 for (int i = 0; i < Q.size(); ++i) {
00599 ++id;
00600 id %= Q.size();
00601 r = popChunkByID(id);
00602 if (r)
00603 return r;
00604 }
00605 return 0;
00606 }
00607
00608 public:
00609 typedef T value_type;
00610
00611 template<bool newconcurrent>
00612 struct rethread {
00613 typedef ChunkedMaster<T, QT, distributed, isStack, chunksize, newconcurrent> WL;
00614 };
00615 template<typename Tnew>
00616 struct retype {
00617 typedef ChunkedMaster<Tnew, QT, distributed, isStack, chunksize, concurrent> WL;
00618 };
00619
00620 ChunkedMaster() : heap(sizeof(Chunk)) {
00621 for (unsigned int i = 0; i < data.size(); ++i) {
00622 p& r = data.get(i);
00623 r.cur = 0;
00624 r.next = 0;
00625 }
00626 }
00627
00628 bool push(value_type val) OPTNOINLINE {
00629 p& n = data.get();
00630 if (n.next && n.next->push_back(val))
00631 return true;
00632 if (n.next)
00633 pushChunk(n.next);
00634 n.next = mkChunk();
00635 bool worked = n.next->push_back(val);
00636 assert(worked);
00637 return true;
00638 }
00639
00640 std::pair<bool, value_type> pop() OPTNOINLINE {
00641 p& n = data.get();
00642 std::pair<bool, value_type> retval;
00643 if (isStack) {
00644 if (n.next && (retval = n.next->pop_back()).first)
00645 return retval;
00646 if(n.next)
00647 delChunk(n.next);
00648 n.next = popChunk();
00649 if (n.next)
00650 return n.next->pop_back();
00651 return std::make_pair(false, value_type());
00652 } else {
00653 if (n.cur && (retval = n.cur->pop_front()).first)
00654 return retval;
00655 if(n.cur)
00656 delChunk(n.cur);
00657 n.cur = popChunk();
00658 if (!n.cur) {
00659 n.cur = n.next;
00660 n.next = 0;
00661 }
00662 if (n.cur)
00663 return n.cur->pop_front();
00664 return std::make_pair(false, value_type());
00665 }
00666 }
00667
00668 bool empty() OPTNOINLINE {
00669 for (unsigned int i = 0; i < data.size(); ++i) {
00670 const p& n = data.get(i);
00671 if (n.cur && !n.cur->empty()) return false;
00672 if (n.next && !n.next->empty()) return false;
00673 }
00674
00675 if (!Q.get().empty()) return false;
00676 for (int i = 0; i < Q.size(); ++i)
00677 if (!Q.get(i).empty())
00678 return false;
00679 return true;
00680 }
00681
00682 bool aborted(value_type val) {
00683 return push(val);
00684 }
00685
00686
00687 template<typename Iter>
00688 void fill_initial(Iter ii, Iter ee) {
00689 for( ; ii != ee; ++ii) {
00690 push(*ii);
00691 }
00692 }
00693 };
00694
00695 template<int chunksize=64, typename T = int, bool concurrent=true>
00696 class ChunkedFIFO : public ChunkedMaster<T, ConExtLinkedQueue, false, false, chunksize, concurrent> {};
00697 WLCOMPILECHECK(ChunkedFIFO);
00698
00699 template<int chunksize=64, typename T = int, bool concurrent=true>
00700 class ChunkedLIFO : public ChunkedMaster<T, ConExtLinkedStack, false, true, chunksize, concurrent> {};
00701 WLCOMPILECHECK(ChunkedLIFO);
00702
00703 template<int chunksize=64, typename T = int, bool concurrent=true>
00704 class dChunkedFIFO : public ChunkedMaster<T, ConExtLinkedQueue, true, false, chunksize, concurrent> {};
00705 WLCOMPILECHECK(dChunkedFIFO);
00706
00707 template<int chunksize=64, typename T = int, bool concurrent=true>
00708 class dChunkedLIFO : public ChunkedMaster<T, ConExtLinkedStack, true, true, chunksize, concurrent> {};
00709 WLCOMPILECHECK(dChunkedLIFO);
00710
00711 template<typename Partitioner = DummyPartitioner, typename T = int, typename ChildWLTy = dChunkedFIFO<>, bool concurrent=true>
00712 class PartitionedWL : private boost::noncopyable {
00713
00714 Partitioner P;
00715 PerCPU<ChildWLTy> Items;
00716 int active;
00717
00718 public:
00719 template<bool newconcurrent>
00720 struct rethread {
00721 typedef PartitionedWL<T, Partitioner, ChildWLTy, newconcurrent> WL;
00722 };
00723
00724 typedef T value_type;
00725
00726 PartitionedWL(const Partitioner& p = Partitioner()) :P(p), active(getSystemThreadPool().getActiveThreads()) {
00727
00728 }
00729
00730 bool push(value_type val) OPTNOINLINE {
00731 unsigned int index = P(val);
00732
00733 return Items.get(index % active).push(val);
00734 }
00735
00736 std::pair<bool, value_type> pop() OPTNOINLINE {
00737 std::pair<bool, value_type> r = Items.get().pop();
00738
00739
00740
00741 return r;
00742 }
00743
00744 std::pair<bool, value_type> try_pop() {
00745 return pop();
00746 }
00747
00748 bool empty() OPTNOINLINE {
00749 return Items.get().empty();
00750 }
00751
00752 bool aborted(value_type val) {
00753 return push(val);
00754 }
00755
00756
00757 template<typename Iter>
00758 void fill_initial(Iter ii, Iter ee) {
00759 for( ; ii != ee; ++ii) {
00760 push(*ii);
00761 }
00762 }
00763
00764 };
00765
00766
00767 }
00768 }
00769
00770 #endif