mrDIMAS

Untitled

Nov 25th, 2017
168
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 6.69 KB | None | 0 0
  1. #include <iostream>
  2. #include <cstdint>
  3. #include <vector>
  4. #include <string>
  5. #include <cassert>
  6. #include <chrono>
  7. #include <memory>
  8. #include <string.h>
  9.  
  10. using namespace std;
  11.  
  12. using Stamp = int32_t;
  13.  
  14. template<typename T>
  15. class Pool;
  16.  
  17. template<typename T>
  18. class Handle {
  19. private:
  20.   friend class Pool<T>;
  21.   int32_t mIndex { 0 };
  22.   Stamp mStamp { 0 };
  23.   Handle(int32_t index, Stamp stamp) : mIndex(index), mStamp(stamp) { }
  24. public:
  25.   Handle() { }
  26. };
  27.  
  28. template<typename T>
  29. class PoolRecord final {
  30. private:
  31.   friend class Pool<T>;
  32.   Stamp mStamp { 0 };
  33. public:
  34.   T mObject;
  35.   template<typename... Args>
  36.   PoolRecord(int stamp, Args&&... args) : mStamp(stamp), mObject(args...) {
  37.   }
  38.   bool IsValid() const noexcept {
  39.     return mStamp != 0;
  40.   }
  41.   const T * operator -> () const {
  42.     return &mObject;
  43.   }
  44.   T * operator -> () {
  45.     return &mObject;
  46.   }
  47. };
  48.  
  49. template<typename T>
  50. class Pool final {
  51. private:
  52.   size_t mSpawnedCount { 0 };
  53.   Stamp mGlobalStamp { 1 };
  54.   PoolRecord<T> * mRecords;
  55.   size_t mRecordCount;
  56.   size_t mCapacity;
  57. public:
  58.   Pool() {
  59.  
  60.   }
  61.   ~Pool() {
  62.     for (size_t i = 0; i < mRecordCount; ++i) {
  63.       mRecords[i].~PoolRecord();
  64.     }
  65.   }
  66.   template<typename... Args>
  67.   Handle<T> Spawn(Args&&... args) {
  68.     // Find free object
  69.     for (size_t i = 0; i < mRecordCount; ++i ) {
  70.       auto & rec = mRecords[i];
  71.       if (rec.mStamp == 0) {
  72.         rec.mStamp = mGlobalStamp++;
  73.         // Reconstruct object via placement new
  74.         new(&rec.mObject) T(args...);
  75.         ++mSpawnedCount;
  76.         return Handle<T>{static_cast<int32_t>(i), rec.mStamp};
  77.       }
  78.     }
  79.     // No free objects, grow pool
  80.     if (mRecordCount >= mCapacity) {
  81.       int oldCapacity = mCapacity;
  82.       if (mCapacity == 0) {
  83.         mCapacity = 1;
  84.       } else {
  85.         mCapacity *= 2;
  86.       }
  87.       PoolRecord<T> * old = mRecords;
  88.       mRecords = reinterpret_cast<PoolRecord<T>*>(malloc(sizeof(PoolRecord<T>) * mCapacity));
  89.       memcpy(mRecords, old, oldCapacity * sizeof(PoolRecord<T>));
  90.       free(old);
  91.     }
  92.     // Obtain new record
  93.     auto & rec = mRecords[mRecordCount];
  94.     rec.mStamp = mGlobalStamp++;
  95.     // Invoke placement new to construct object
  96.     new(&rec.mObject) T(args...);
  97.     ++mSpawnedCount;
  98.     ++mRecordCount;
  99.     return Handle<T>{static_cast<int32_t>(mRecordCount - 1), rec.mStamp};
  100.   }
  101.   void Return(const Handle<T> & handle) {
  102.     if (static_cast<size_t>(handle.mIndex) >= mRecordCount) {
  103.       throw runtime_error("Handle has invalid index!");
  104.     }
  105.     auto & rec = mRecords[handle.mIndex];
  106.     rec.mObject.~T(); // destruct
  107.     rec.mStamp = 0;
  108.     --mSpawnedCount;
  109.   }
  110.   bool IsValid(const Handle<T> & handle) {
  111.     if (static_cast<size_t>(handle.mIndex) >= mRecordCount) {
  112.       throw runtime_error("Handle has invalid index!");
  113.     }
  114.     return handle.mStamp == mRecords[handle.mIndex].mStamp;
  115.   }
  116.   T & operator[](const Handle<T> & handle) const {
  117.     return mRecords[handle.mIndex].mObject;
  118.   }
  119.   size_t GetSize() const noexcept {
  120.     return mRecordCount;
  121.   }
  122.   size_t GetSpawnedCount() const noexcept {
  123.     return mSpawnedCount;
  124.   }
  125.   size_t GetCapacity() const noexcept {
  126.     return mCapacity;
  127.   }  
  128.   PoolRecord<T> * GetRecords() noexcept {
  129.     return mRecords;
  130.   }
  131.   // range-based for
  132.   PoolRecord<T> * begin() {
  133.     return mRecords;
  134.   }
  135.   PoolRecord<T> * end() {
  136.     return mRecords + mRecordCount;
  137.   }
  138.   // Use this only to obtain handle by 'this' pointer inside class method
  139.   // Do not rely on pointers to objects in pool, they can suddenly become invalid
  140.   // this pointer can be used, because it is always valid
  141.   Handle<T> HandleByPointer(const T * ptr) const {
  142.     for (size_t i = 0; i < mRecordCount; ++i) {
  143.       if (&mRecords[i].mObject == ptr) {
  144.         return Handle<T>(static_cast<int32_t>(i), mRecords[i].mStamp);
  145.       }
  146.     }
  147.   }
  148. };
  149.  
  150. class PoolableNode {
  151. private:
  152.   string mName;
  153.   Handle<PoolableNode> mParent;
  154.   vector<Handle<PoolableNode>> mChildren;
  155.   // pointer to pool always valid, since node allocated inside the pool
  156.   Pool<PoolableNode> * mPool { nullptr };
  157. public:
  158.   PoolableNode(Pool<PoolableNode> * pool) : mPool(pool) {
  159.  
  160.   }
  161.   virtual ~PoolableNode() {
  162.     for(int i = 0; i < mChildren.size(); ++i) {    
  163.       mPool->Return(mChildren[i]);
  164.     }
  165.   }
  166.   void SetName(const string & name) {
  167.     mName = name;
  168.   }
  169.   void AttachTo(const Handle<PoolableNode> & parentHandle) {
  170.     auto & parent = (*mPool)[parentHandle];
  171.     mParent = parentHandle;
  172.     parent.mChildren.push_back(mPool->HandleByPointer(this));
  173.   }
  174. };
  175.  
  176. class OrdinaryNode : public enable_shared_from_this<OrdinaryNode> {
  177. private:
  178.   string mName;
  179.   weak_ptr<OrdinaryNode> mParent;
  180.   vector<shared_ptr<OrdinaryNode>> mChildren;
  181. public:
  182.   OrdinaryNode() {
  183.  
  184.   }
  185.   virtual ~OrdinaryNode() {
  186.    
  187.   }
  188.   void SetName(const string & name) {
  189.     mName = name;
  190.   }
  191.   void AttachTo(const shared_ptr<OrdinaryNode> & parent) {
  192.     parent->mChildren.push_back(shared_from_this());
  193.     mParent = parent;
  194.   }
  195. };
  196.  
  197. int main(int argc, char **argv) {
  198.   // Sanity tests
  199.   {
  200.     Pool<PoolableNode> pool;
  201.  
  202.     auto handle = pool.Spawn(&pool);
  203.     assert(pool.IsValid(handle));
  204.     assert(pool.GetSize() == 1);
  205.  
  206.     pool.Return(handle);
  207.     assert(!pool.IsValid(handle));
  208.     assert(pool.GetSize() == 1);
  209.     assert(pool.GetSpawnedCount() == 0);
  210.  
  211.     auto newHandle = pool.Spawn(&pool);
  212.     assert(pool.IsValid(newHandle));
  213.     assert(pool.GetSpawnedCount() == 1);
  214.     pool.Return(newHandle);
  215.   }
  216.  
  217.   // Performance test
  218.   {
  219.     constexpr int count = 10000;
  220.  
  221.     // PoolableNode
  222.     {
  223.       Pool<PoolableNode> pool;
  224.  
  225.       auto lastTime = chrono::high_resolution_clock::now();
  226.       for (int i = 0; i < count; ++i) {
  227.         auto parent = pool.Spawn(&pool);
  228.         pool[parent].SetName("Parent");
  229.         auto child = pool.Spawn(&pool);
  230.         pool[child].SetName("Child");
  231.         pool[child].AttachTo(parent);
  232.         pool.Return(parent);
  233.       }
  234.  
  235.       cout << "PoolableNode: " << chrono::duration_cast<chrono::microseconds>(chrono::high_resolution_clock::now() - lastTime).count() << " microseconds" << endl;
  236.     }
  237.  
  238.     // OrdinaryNode
  239.     {
  240.       auto lastTime = chrono::high_resolution_clock::now();
  241.  
  242.       for (int i = 0; i < count; ++i) {
  243.         auto parent = make_shared<OrdinaryNode>();
  244.         parent->SetName("Parent");
  245.         auto child = make_shared<OrdinaryNode>();
  246.         child->SetName("Child");
  247.         child->AttachTo(parent);
  248.         parent.reset();
  249.       }
  250.  
  251.       cout << "OrdinaryNode: " << chrono::duration_cast<chrono::microseconds>(chrono::high_resolution_clock::now() - lastTime).count() << " microseconds" << endl;
  252.     }
  253.   }
  254.  
  255.    
  256.   system("pause");
  257.   return 0;
  258. }
Advertisement
Add Comment
Please, Sign In to add comment