allocPool: Fix threading issue and add benchmark.

This commit is contained in:
Timothée Leclaire-Fournier 2024-03-01 16:30:51 -05:00
parent dead668fb7
commit 6cd26d5ee9
6 changed files with 72 additions and 3 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.idea/*
cmake-build-debug/*

View File

@ -12,3 +12,19 @@ This pool uses a hashmap and a pivot to make returnPtr(ptr) extremely fast.
It will automatically grow when the max capacity is reached, though there will It will automatically grow when the max capacity is reached, though there will
be a performance penalty. be a performance penalty.
## Performance
With a simple stub class and a pool of 10000 objects, using the pool to take a pointer
and give it back takes 3 ms vs 19 ms when allocating and deallocating by hand.
```
class stub {
public:
stub() {
for (int j{}; j < 1000; j++) { i++; }
};
void reset() {}
private:
int i = 15;
};
```

View File

@ -47,6 +47,7 @@ public:
private: private:
std::vector<T *> vec; std::vector<T *> vec;
std::mutex positionMapMutex;
std::unordered_map<T *, size_t> positionMap; std::unordered_map<T *, size_t> positionMap;
size_t pivot; size_t pivot;
@ -59,7 +60,7 @@ private:
threads.reserve(amountOfThreads); threads.reserve(amountOfThreads);
for (size_t i{}; i < amountOfThreads; i++) for (size_t i{}; i < amountOfThreads; i++)
threads.emplace_back(&allocPool::initObjects, this, i, amountPerThreads); threads.emplace_back(&allocPool::initObjects, this, i * amountPerThreads, amountPerThreads);
for (auto &t: threads) for (auto &t: threads)
t.join(); t.join();
@ -72,6 +73,9 @@ private:
for (size_t i{}; i < amount; i++) { for (size_t i{}; i < amount; i++) {
// TODO: Be more cache friendly by making a vector per thread, then doing memcpy into the original vector. // TODO: Be more cache friendly by making a vector per thread, then doing memcpy into the original vector.
vec[startIdx + i] = new T; vec[startIdx + i] = new T;
}
std::lock_guard<std::mutex> guard(positionMapMutex);
for (size_t i{}; i < amount; i++) {
positionMap[vec[startIdx + i]] = i; positionMap[vec[startIdx + i]] = i;
} }
} }

View File

@ -1,7 +1,39 @@
#include <chrono>
#include <iostream>
#include "tests.hpp" #include "tests.hpp"
int main() { int main() {
tests t; tests t;
t.runTests(); t.runTests();
auto startSlow{std::chrono::high_resolution_clock::now()};
stub *ptr{};
for (size_t i{}; i < 10000; i++) {
ptr = new stub();
delete ptr;
}
auto endSlow{std::chrono::high_resolution_clock::now()};
std::cout << "Time (milliseconds) required for allocations without pool: " <<
std::chrono::duration_cast<std::chrono::milliseconds>(endSlow - startSlow).count() << "\n";
auto startAlloc{std::chrono::high_resolution_clock::now()};
allocPool<stub> a(10000);
auto endAlloc{std::chrono::high_resolution_clock::now()};
auto startFast{std::chrono::high_resolution_clock::now()};
for (size_t i{}; i < 10000; i++) {
ptr = a.getPtr();
a.returnPtr(ptr);
}
auto endFast{std::chrono::high_resolution_clock::now()};
std::cout << "Time (milliseconds) required for allocations with pool: " <<
std::chrono::duration_cast<std::chrono::milliseconds>(endFast - startFast).count() << "\n";
std::cout << "Time (milliseconds) required for real allocations when constructing pool: " <<
std::chrono::duration_cast<std::chrono::milliseconds>(endAlloc - startAlloc).count() << "\n";
return 0; return 0;
} }

View File

@ -25,3 +25,15 @@ ADD_TEST(allocPoolSimple) {
pool.returnPtr(var4); pool.returnPtr(var4);
pool.returnPtr(var3); pool.returnPtr(var3);
} }
ADD_TEST(allocPoolSimple2) {
allocPool<stub> pool(10);
auto *var1{pool.getPtr()};
auto *var2{pool.getPtr()};
auto *var3{pool.getPtr()};
pool.returnPtr(var2);
auto *var4{pool.getPtr()};
pool.returnPtr(var1);
pool.returnPtr(var4);
pool.returnPtr(var3);
}

View File

@ -32,8 +32,11 @@ public:
class stub { class stub {
public: public:
stub() = default; stub() {
for (int j{}; j < 1000; j++) { i++; }
};
void reset() {} void reset() {}
private: private:
int i = 15; int i = 15;
}; };