mrDIMAS

Pool

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