2024-03-01 14:21:52 -05:00
|
|
|
# allocPool - A simple & high performance object pool using modern C++
|
|
|
|
|
2024-03-01 15:19:52 -05:00
|
|
|
This is allocPool, a pool of objects in a single header file that allow you to
|
|
|
|
avoid expensive allocations during runtime. This preallocates objects in the
|
|
|
|
constructor (with threads) then offers you two functions: `getPtr()` and `returnPtr(ptr)`.
|
2024-03-01 14:21:52 -05:00
|
|
|
|
|
|
|
Using C++ concepts, we can use templates and require the class given to have a
|
2024-03-02 12:48:49 -05:00
|
|
|
default constructor and to have a `.reset()` function. It will be used to clean the
|
2024-03-12 11:38:07 -04:00
|
|
|
objects before giving them to another caller instead of reallocating.
|
2024-03-01 14:21:52 -05:00
|
|
|
|
2024-03-02 12:48:49 -05:00
|
|
|
We avoid false sharing by keeping a high amount of work per thread. This should
|
2024-03-12 11:38:07 -04:00
|
|
|
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.
|
2024-03-01 14:21:52 -05:00
|
|
|
|
2024-03-12 11:38:07 -04:00
|
|
|
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.
|
2024-03-01 16:30:51 -05:00
|
|
|
|
|
|
|
## Performance
|
|
|
|
With a simple stub class and a pool of 10000 objects, using the pool to take a pointer
|
2024-03-02 12:48:49 -05:00
|
|
|
and give it back for each element is significantly faster than doing it by hand.
|
|
|
|
|
2024-03-01 16:30:51 -05:00
|
|
|
```
|
|
|
|
class stub {
|
|
|
|
public:
|
|
|
|
stub() {
|
|
|
|
for (int j{}; j < 1000; j++) { i++; }
|
|
|
|
};
|
|
|
|
void reset() {}
|
|
|
|
|
|
|
|
private:
|
|
|
|
int i = 15;
|
|
|
|
};
|
2024-03-02 12:48:49 -05:00
|
|
|
```
|
2024-03-03 14:40:55 -05:00
|
|
|
On Linux:
|
2024-03-02 12:48:49 -05:00
|
|
|
```
|
|
|
|
Time (milliseconds) required for allocations without pool: 21
|
|
|
|
Time (milliseconds) required for allocations with pool: 3
|
|
|
|
Time (milliseconds) required for real allocations when constructing pool: 9
|
|
|
|
```
|
2024-03-03 14:40:55 -05:00
|
|
|
On Windows:
|
|
|
|
```
|
|
|
|
Time (milliseconds) required for allocations without pool: 62
|
|
|
|
Time (milliseconds) required for allocations with pool: 6
|
|
|
|
Time (milliseconds) required for real allocations when constructing pool: 51
|
|
|
|
```
|
2024-03-02 12:48:49 -05:00
|
|
|
|
2024-03-02 13:25:48 -05:00
|
|
|
This trivial example shows some performance improvements that would be much more
|
2024-03-03 00:11:31 -05:00
|
|
|
important should the allocation and construction/destruction of the objects be more
|
|
|
|
complex.
|
2024-03-02 12:48:49 -05:00
|
|
|
|
2024-03-03 14:40:55 -05:00
|
|
|
In the case where the allocator is very fast (such as glibc's on Linux) this approach
|
|
|
|
may not be necessary, but in the case of slow allocators (such as Windows' default), it
|
|
|
|
could be interesting to consider such an approach.
|
|
|
|
|
2024-03-02 12:48:49 -05:00
|
|
|
## Safety
|
|
|
|
AddressSanitizer, LeakSanitizer and ThreadSanitizer have been used to ensure the safety
|
|
|
|
of the class. Tests have been added to ensure the correct behavior in all cases.
|