In an application i generate a lot of subproblems exhaustively and solve them using "std::set" operations. For this I need to "insert" and "find" elements and also "iterate" over the sorted list.
The problem is that for each of the millions of subproblems the "std::set" implementation allocates new memory each time I insert an element in the set which makes the whole application very slow:
{ // allocate a non-value node
_Nodeptr _Pnode = this->_Getal().allocate(1); // <- bottleneck of the program
Is there some stl-structure that allows me to to the above operations in "O(log(n))" while not reallocating any memory?
Using a custom allocator seems a way to reduce the amount of time spent for building and releasing a std::set<...>
. Below is a complete demo of a simple allocator together with a program profiling the resulting times.
#include <algorithm>
#include <chrono>
#include <cstdlib>
#include <iostream>
#include <iterator>
#include <memory>
#include <set>
#include <vector>
// ----------------------------------------------------------------------------
template <typename T, std::size_t pool_size = 1024>
class pool_allocator
{
private:
std::vector<T*> d_pools;
T* d_next;
T* d_end;
public:
template <typename O>
struct rebind {
typedef pool_allocator<O, pool_size> other;
};
pool_allocator(): d_next(), d_end() {}
~pool_allocator() {
std::for_each(this->d_pools.rbegin(), this->d_pools.rend(),
[](T* memory){ operator delete(memory); });
}
typedef T value_type;
T* allocate(std::size_t n) {
if (std::size_t(this->d_end - this->d_next) < n) {
if (pool_size < n) {
// custom allocation for bigger number of objects
this->d_pools.push_back(static_cast<T*>(operator new(sizeof(T) * n)));
return this->d_pools.back();
}
this->d_pools.push_back(static_cast<T*>(operator new(sizeof(T) * pool_size)));
this->d_next = this->d_pools.back();
this->d_end = this->d_next + pool_size;
}
T* rc(this->d_next);
this->d_next += n;
return rc;
}
void deallocate(T*, std::size_t) {
// this could try to recycle buffers
}
};
// ----------------------------------------------------------------------------
template <typename Allocator>
void time(char const* name, std::vector<int> const& random) {
std::cout << "running " << name << std::flush;
using namespace std::chrono;
high_resolution_clock::time_point start(high_resolution_clock::now());
std::size_t size(0);
{
std::set<int, std::less<int>, Allocator> values;
for (int value: random) {
values.insert(value);
}
size = values.size();
}
high_resolution_clock::time_point end(high_resolution_clock::now());
std::cout << ": size=" << size << " time="
<< duration_cast<milliseconds>(end - start).count() << "ms\n";
}
// ----------------------------------------------------------------------------
int main()
{
std::cout << "preparing..." << std::flush;
std::size_t count(10000000);
std::vector<int> random;
random.reserve(count);
std::generate_n(std::back_inserter(random), count, [](){ return std::rand(); });
std::cout << "done\n";
time<std::allocator<int>>("default allocator ", random);
time<pool_allocator<int, 32>>("custom allocator (32) ", random);
time<pool_allocator<int, 256>>("custom allocator (256) ", random);
time<pool_allocator<int, 1024>>("custom allocator (1024)", random);
time<pool_allocator<int, 2048>>("custom allocator (2048)", random);
time<pool_allocator<int, 4096>>("custom allocator (4096)", random);
time<std::allocator<int>>("default allocator ", random);
}
// results from clang/libc++:
// preparing...done
// running default allocator : size=10000000 time=13927ms
// running custom allocator (32) : size=10000000 time=9260ms
// running custom allocator (256) : size=10000000 time=9511ms
// running custom allocator (1024): size=10000000 time=9172ms
// running custom allocator (2048): size=10000000 time=9153ms
// running custom allocator (4096): size=10000000 time=9599ms
// running default allocator : size=10000000 time=13730ms
// results from gcc/libstdc++:
// preparing...done
// running default allocator : size=10000000 time=15814ms
// running custom allocator (32) : size=10000000 time=10868ms
// running custom allocator (256) : size=10000000 time=10229ms
// running custom allocator (1024): size=10000000 time=10556ms
// running custom allocator (2048): size=10000000 time=10392ms
// running custom allocator (4096): size=10000000 time=10664ms
// running default allocator : size=10000000 time=17941ms
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With