diff --git a/README.md b/README.md index a35f49d..12354ac 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,22 @@ constructor (with threads) then offers you two functions: `getPtr()` and `return Using C++ concepts, we can use templates and require the class given to have a default constructor and to have a `.reset()` function. It will be used to clean the -objects before giving them to another caller. +objects before giving them to another caller instead of reallocating. We avoid false sharing by keeping a high amount of work per thread. This should -lead to cache lines not being shared between threads. While this pool uses a hashmap -and a pivot to make `returnPtr(ptr)` extremely fast, the construction's main bottleneck is -in the locking and unlocking of the hashmap's mutex. We need to do this since we cannot -write in a `std::unordered_map` at different hashes concurrently. +lead to cache lines not being shared between threads. In the advent that cache +lines would overlap, we use a smaller amount of threads with a bit more work +per thread. This option is enabled with the `bool enableFalseSharingMitigations` +parameter in the constructor. It is on by default and can be disabled in the advent +that you very big objects where writing pointers in the `std::vector` will not be the +bottleneck. -It will automatically grow when the max capacity is reached, though there will -be a performance penalty. +While this pool uses a hashmap and a pivot to make `returnPtr(ptr)` extremely fast, +when saving the pointers, the main bottleneck is in the locking and unlocking of the +hashmap's mutex. We need to do this since we cannot write in a `std::unordered_map` +at different hashes concurrently. + +It will automatically grow to twice its size when the max capacity is reached. ## Performance With a simple stub class and a pool of 10000 objects, using the pool to take a pointer diff --git a/allocPool.hpp b/allocPool.hpp index b16674e..a8cdb3a 100644 --- a/allocPool.hpp +++ b/allocPool.hpp @@ -20,8 +20,8 @@ template requires std::default_initializable && resetable class allocPool { public: - explicit allocPool(size_t allocNumbers) - : vec(allocNumbers), pivot{allocNumbers} { + explicit allocPool(size_t allocNumbers, bool enableFalseSharingMitigations = true) + : vec(allocNumbers), pivot{allocNumbers}, falseSharingMitigations{enableFalseSharingMitigations} { memset(&(vec[0]), 0, sizeof(vec[0]) * vec.size()); initArray(allocNumbers); } @@ -57,6 +57,7 @@ private: std::mutex positionMapMutex; std::unordered_map positionMap; size_t pivot; + bool falseSharingMitigations; void initArray(size_t arrSize) { size_t amountOfThreads{std::thread::hardware_concurrency()}; @@ -68,8 +69,10 @@ private: threads.reserve(amountOfThreads); // We try to avoid false sharing by defining a minimum size. - amountPerThread = minObjPerThread > amountPerThread ? minObjPerThread : amountPerThread; - amountOfThreads = arrSize / amountPerThread; + if (falseSharingMitigations) { + amountPerThread = minObjPerThread > amountPerThread ? minObjPerThread : amountPerThread; + amountOfThreads = arrSize / amountPerThread; + } for (size_t i{}; i < amountOfThreads; i++) threads.emplace_back(&allocPool::initObjects, this, i * amountPerThread, amountPerThread);