00001 
00023 #ifndef GALOIS_RUNTIME_ALTCHUNKED_H
00024 #define GALOIS_RUNTIME_ALTCHUNKED_H
00025 
00026 #include "Galois/FixedSizeRing.h"
00027 #include "Galois/Threads.h"
00028 #include "Galois/Runtime/PerThreadStorage.h"
00029 #include "Galois/Runtime/ll/CompilerSpecific.h"
00030 #include "Galois/Runtime/ll/PtrLock.h"
00031 #include "Galois/Runtime/mm/Mem.h"
00032 #include "WLCompileCheck.h"
00033 
00034 namespace Galois {
00035 namespace WorkList {
00036 
00037 struct ChunkHeader {
00038   ChunkHeader* next;
00039   ChunkHeader* prev;
00040 };
00041 
00042 #if 0
00043 class AtomicChunkedDeque {
00044   Runtime::LL::PtrLock<ChunkHeader, true> head;
00045   ChunkHeader* volatile tail;
00046 
00047 public:
00048 
00049   void push_front(ChunkHeader* obj) {
00050     head.lock();
00051     obj->prev = 0;
00052     obj->next = head.getValue();
00053     if (obj->next)
00054       obj->next->prev = obj;
00055     if (!tail)
00056       tail = obj;
00057     head.unlock_and_set(obj);
00058   }
00059 
00060   void push_back(ChunkHeader* obj) {
00061     head.lock();
00062     obj->next = 0;
00063     obj->prev = tail;
00064     if (obj->prev)
00065       obj->prev->next = obj;
00066     tail = obj;
00067     if (head.getValue())
00068       head.unlock();
00069     else
00070       head.unlock_and_set(obj);
00071   }
00072 
00073   ChunkHeader* pop_back() {
00074     
00075     if (!tail) return 0;
00076     head.lock();
00077     ChunkHeader* retval = tail;
00078     if (retval) {
00079       if (retval->prev)
00080         retval->prev->next = 0;
00081       tail = retval->prev;
00082       if (head.getValue() == retval)
00083         head.unlock_and_clear();
00084       else
00085         head.unlock();
00086       
00087       retval->prev = retval->next = 0;
00088       return retval;
00089     } else {
00090       head.unlock();
00091       return 0;
00092     }
00093   }
00094 
00095   ChunkHeader* pop_front() {
00096     
00097     if (!tail) return 0; 
00098     head.lock();
00099     ChunkHeader* retval = head.getValue();
00100     if (retval) {
00101       if (retval->next)
00102         retval->next->prev = 0;
00103       if (tail == retval)
00104         tail = 0;
00105       head.unlock_and_set(retval->next);
00106       
00107       retval->prev = retval->next = 0;
00108       return retval;
00109     } else {
00110       head.unlock();
00111       return 0;
00112     }
00113   }
00114 };
00115 #endif
00116 
00117 class AltChunkedQueue {
00118   Runtime::LL::PtrLock<ChunkHeader, true> head;
00119   ChunkHeader* tail;
00120 
00121   void prepend(ChunkHeader* C) {
00122     
00123     ChunkHeader* t = C;
00124     while (t->next) { t = t->next; }
00125     head.lock();
00126     t->next = head.getValue();
00127     if (!t->next)
00128       tail = t;
00129     head.unlock_and_set(C);
00130   }
00131 
00132 public:
00133   AltChunkedQueue(): tail(0) { }
00134 
00135   bool empty() const {
00136     return !tail;
00137   }
00138 
00139   void push(ChunkHeader* obj) {
00140     head.lock();
00141     obj->next = 0;
00142     if (tail) {
00143       tail->next = obj;
00144       tail = obj;
00145       head.unlock();
00146     } else {
00147       assert(!head.getValue());
00148       tail = obj;
00149       head.unlock_and_set(obj);
00150     }
00151   }
00152 
00153   ChunkHeader* pop() {
00154     
00155     if (empty()) return 0;
00156 
00157     head.lock();
00158     ChunkHeader* h = head.getValue();
00159     if (!h) {
00160       head.unlock();
00161       return 0;
00162     }
00163     if (tail == h) {
00164       tail = 0;
00165       assert(!h->next);
00166       head.unlock_and_clear();
00167     } else {
00168       head.unlock_and_set(h->next);
00169       h->next = 0;
00170     }
00171     return h;
00172   }
00173 
00174   ChunkHeader* stealAllAndPop(AltChunkedQueue& victim) {
00175     
00176     if (victim.empty()) return 0;
00177     
00178     victim.head.lock();
00179     ChunkHeader* C = victim.head.getValue();
00180     if (C)
00181       victim.tail = 0;
00182     victim.head.unlock_and_clear();
00183     if (!C) return 0; 
00184     ChunkHeader* retval = C;
00185     C = C->next;
00186     retval->next = 0;
00187     if (!C) return retval; 
00188     prepend(C);
00189     return retval;
00190   }
00191 
00192   ChunkHeader* stealHalfAndPop(AltChunkedQueue& victim) {
00193     
00194     if (victim.empty()) return 0;
00195     
00196     victim.head.lock();
00197     ChunkHeader* C = victim.head.getValue();
00198     ChunkHeader* ntail = C;
00199     bool count = false;
00200     while (C) { 
00201       C = C->next;
00202       if (count)
00203         ntail = ntail->next;
00204       count = !count;
00205     }
00206     if (ntail) {
00207       C = ntail->next;
00208       ntail->next = 0;
00209       victim.tail = ntail;
00210     }
00211     victim.head.unlock();
00212     if (!C) return 0; 
00213     ChunkHeader* retval = C;
00214     C = C->next;
00215     retval->next = 0;
00216     if (!C) return retval; 
00217     prepend(C);
00218     return retval;
00219   }
00220 };
00221 
00222 class AltChunkedStack {
00223   Runtime::LL::PtrLock<ChunkHeader, true> head;
00224 
00225   void prepend(ChunkHeader* C) {
00226     
00227     ChunkHeader* tail = C;
00228     while (tail->next) { tail = tail->next; }
00229     head.lock();
00230     tail->next = head.getValue();
00231     head.unlock_and_set(C);
00232   }
00233 
00234 public:
00235   bool empty() const {
00236     return !head.getValue();
00237   }
00238 
00239   void push(ChunkHeader* obj) {
00240     ChunkHeader* oldhead = 0;
00241     do {
00242       oldhead = head.getValue();
00243       obj->next = oldhead;
00244     } while (!head.CAS(oldhead, obj));
00245   }
00246 
00247   ChunkHeader* pop() {
00248     
00249     if (empty()) return 0;
00250 
00251     
00252     head.lock();
00253     ChunkHeader* retval = head.getValue();
00254     ChunkHeader* setval = 0;
00255     if (retval) {
00256       setval = retval->next;
00257       retval->next = 0;
00258     }
00259     head.unlock_and_set(setval);
00260     return retval;
00261   }
00262 
00263   ChunkHeader* stealAllAndPop(AltChunkedStack& victim) {
00264     
00265     if (victim.empty()) return 0;
00266     
00267     victim.head.lock();
00268     ChunkHeader* C = victim.head.getValue();
00269     victim.head.unlock_and_clear();
00270     if (!C) return 0; 
00271     ChunkHeader* retval = C;
00272     C = C->next;
00273     retval->next = 0;
00274     if (!C) return retval; 
00275     prepend(C);
00276     return retval;
00277   }
00278 
00279   ChunkHeader* stealHalfAndPop(AltChunkedStack& victim) {
00280     
00281     if (victim.empty()) return 0;
00282     
00283     victim.head.lock();
00284     ChunkHeader* C = victim.head.getValue();
00285     ChunkHeader* ntail = C;
00286     bool count = false;
00287     while (C) { 
00288       C = C->next;
00289       if (count)
00290         ntail = ntail->next;
00291       count = !count;
00292     }
00293     if (ntail) {
00294       C = ntail->next;
00295       ntail->next = 0;
00296     }
00297     victim.head.unlock();
00298     if (!C) return 0; 
00299     ChunkHeader* retval = C;
00300     C = C->next;
00301     retval->next = 0;
00302     if (!C) return retval; 
00303     prepend(C);
00304     return retval;
00305   }
00306 };
00307 
00308 template<typename InnerWL>
00309 class StealingQueue : private boost::noncopyable {
00310   Runtime::PerThreadStorage<std::pair<InnerWL, unsigned> > local;
00311 
00312   GALOIS_ATTRIBUTE_NOINLINE
00313   ChunkHeader* doSteal() {
00314     std::pair<InnerWL, unsigned>& me = *local.getLocal();
00315     unsigned id = Runtime::LL::getTID();
00316     unsigned pkg = Runtime::LL::getPackageForSelf(id);
00317     unsigned num = Galois::getActiveThreads();
00318 
00319     
00320     for (unsigned eid = id + 1; eid < num; ++eid) {
00321       if (Runtime::LL::getPackageForThread(eid) == pkg) {
00322         ChunkHeader* c = me.first.stealHalfAndPop(local.getRemote(eid)->first);
00323         if (c)
00324           return c;
00325       }
00326     }
00327     for (unsigned eid = 0; eid < id; ++eid) {
00328       if (Runtime::LL::getPackageForThread(eid) == pkg) {
00329         ChunkHeader* c = me.first.stealHalfAndPop(local.getRemote(eid)->first);
00330         if (c)
00331           return c;
00332       }
00333     }
00334 
00335     
00336     if (Runtime::LL::isPackageLeaderForSelf(id)) {
00337       unsigned eid = (id + me.second) % num;
00338       ++me.second;
00339       if (id != eid && Runtime::LL::isPackageLeader(eid)) {
00340         ChunkHeader* c = me.first.stealAllAndPop(local.getRemote(eid)->first);
00341         if (c)
00342           return c;
00343       }
00344     }
00345     return 0;
00346   }
00347 
00348 public:
00349   void push(ChunkHeader* c) {
00350     local.getLocal()->first.push(c);
00351   }
00352 
00353   ChunkHeader* pop() {
00354     if (ChunkHeader* c = local.getLocal()->first.pop())
00355       return c;
00356     return doSteal();
00357   }
00358 };
00359 
00360 template<bool IsLocallyLIFO, int ChunkSize, typename Container, typename T>
00361 struct AltChunkedMaster : private boost::noncopyable {
00362   template<typename _T>
00363   struct retype { typedef AltChunkedMaster<IsLocallyLIFO, ChunkSize, Container, _T> type; };
00364 
00365   template<bool _concurrent>
00366   struct rethread { typedef AltChunkedMaster<IsLocallyLIFO, ChunkSize, Container, T> type; };
00367 
00368   template<int _chunk_size>
00369   struct with_chunk_size { typedef AltChunkedMaster<IsLocallyLIFO, _chunk_size, Container, T> type; };
00370 
00371 private:
00372   class Chunk : public ChunkHeader, public Galois::FixedSizeRing<T, ChunkSize> {};
00373 
00374   Runtime::MM::FixedSizeAllocator heap;
00375   Runtime::PerThreadStorage<std::pair<Chunk*, Chunk*> > data;
00376   Container worklist;
00377 
00378   Chunk* mkChunk() {
00379     return new (heap.allocate(sizeof(Chunk))) Chunk();
00380   }
00381   
00382   void delChunk(Chunk* C) {
00383     C->~Chunk();
00384     heap.deallocate(C);
00385   }
00386 
00387   void swapInPush(std::pair<Chunk*, Chunk*>& d) {
00388     if (!IsLocallyLIFO)
00389       std::swap(d.first, d.second);
00390   }
00391 
00392   Chunk*& getPushChunk(std::pair<Chunk*, Chunk*>& d) {
00393     if (!IsLocallyLIFO)
00394       return d.second;
00395     else
00396       return d.first;
00397   }
00398 
00399   Chunk*& getPopChunk(std::pair<Chunk*, Chunk*>& d) {
00400     return d.first;
00401   }
00402 
00403   bool doPush(Chunk* c, const T& val) {
00404     return c->push_back(val);
00405   }
00406 
00407   Galois::optional<T> doPop(Chunk* c) {
00408     if (!IsLocallyLIFO)
00409       return c->extract_front();
00410     else
00411       return c->extract_back();
00412   }
00413 
00414   void push_internal(std::pair<Chunk*, Chunk*>& tld, Chunk*& n, const T& val) {
00415     
00416     if (n && doPush(n, val))
00417       return;
00418     
00419     if (n)
00420       worklist.push(static_cast<ChunkHeader*>(n));
00421     
00422     n = mkChunk();
00423     
00424     doPush(n, val);
00425   }
00426 
00427 public:
00428   typedef T value_type;
00429 
00430   AltChunkedMaster() : heap(sizeof(Chunk)) {}
00431 
00432   void push(value_type val) {
00433     std::pair<Chunk*, Chunk*>& tld = *data.getLocal();
00434     Chunk*& n = getPushChunk(tld);
00435     push_internal(tld, n, val);
00436   }
00437 
00438   template<typename Iter>
00439   void push(Iter b, Iter e) {
00440     std::pair<Chunk*, Chunk*>& tld = *data.getLocal();
00441     Chunk*& n = getPushChunk(tld);
00442     while (b != e)
00443       push_internal(tld, n, *b++);
00444   }
00445 
00446   template<typename RangeTy>
00447   void push_initial(const RangeTy& range) {
00448     auto rp = range.local_pair();
00449     push(rp.first, rp.second);
00450   }
00451 
00452   Galois::optional<value_type> pop() {
00453     std::pair<Chunk*, Chunk*>& tld = *data.getLocal();
00454     Chunk*& n = getPopChunk(tld);
00455     Galois::optional<value_type> retval;
00456     
00457     if (n && (retval = doPop(n)))
00458       return retval;
00459     
00460     if (n)
00461       delChunk(n);
00462     
00463     n = static_cast<Chunk*>(worklist.pop());
00464     if (n && (retval = doPop(n)))
00465       return retval;
00466     
00467     swapInPush(tld);
00468     if (n)
00469       retval = doPop(n);
00470     return retval;
00471   }
00472 };
00473 
00474 template<int ChunkSize=64, typename T = int>
00475 class AltChunkedLIFO : public AltChunkedMaster<true, ChunkSize, StealingQueue<AltChunkedStack>, T> {};
00476 GALOIS_WLCOMPILECHECK(AltChunkedLIFO)
00477 
00478 template<int ChunkSize=64, typename T = int>
00479 class AltChunkedFIFO : public AltChunkedMaster<false, ChunkSize, StealingQueue<AltChunkedQueue>, T> {};
00480 GALOIS_WLCOMPILECHECK(AltChunkedFIFO)
00481 
00482 } 
00483 } 
00484 #endif