I have a class that looks like this:
typedef std::list<char*> PtrList;
class Foo
{
public:
void DoStuff();
private:
PtrList m_list;
PtrList::iterator m_it;
};
The function DoStuff()
basically adds elements to m_list
or erases elements from it, finds an iterator to some special element in it and stores it in m_it
. It is important to note that each value of m_it
is used in every following call of DoStuff()
.
So what's the problem?
Everything works, except that profiling shows that the operator new
is invoked too much due to list::push_back()
called from DoStuff()
.
To increase performance I want to preallocate memory for m_list
in the initialization of Foo
as I would do if it were an std::vector
. The problem is that this would introduce new problems such as:
insert
and erase
of elements.m_it
becomes invalid as soon as the vector is changed from one call to DoStuff()
to the next. EDIT: Alan Stokes suggested to use an index instead of an iterator, solving this issue.My solution: the simplest solution I could think of is to implement a pool of objects that also has a linked-list functionality. This way I get a linked list and can preallocate memory for it.
Am I missing something or is it really the simplest solution? I'd rather not "re-invent the wheel", and use a standard solution instead, if it exists.
Any thoughts, workarounds or enlightening comments would be appreciated!
std::vector::reserveRequests that the vector capacity be at least enough to contain n elements. If n is greater than the current vector capacity, the function causes the container to reallocate its storage increasing its capacity to n (or greater).
vector::reserve does allocate memory, so your question about reserving memory without allocating is incorrect. The point is that reserving memory can be done without changing the vectors size. Basically a vector has two sizes, it's size and it's capacity.
An std::vector manages its own memory. You can use the reserve() and resize() methods to have it allocate enough memory to fit a given amount of items: std::vector<int> vec1; vec1. reserve(30); // Allocate space for 30 items, but vec1 is still empty.
Hence std::list provides some extra functions for Sorting, Splicing, Removing elements and identifying unique elements. Vector provides the random access and hence can be used with STL algorithms that uses Random Access Iterators.
I think you are using wrong the container.
If you want fast push back then don't automatically assume that you need a linked list, a linked list is a slow container, it is basically suitable for reordering.
A better container is a std::deque. A deque is basically a array of arrays. It allocates a block of memory and occupies it when you push back, when it runs out it will allocate another block. This means that it only allocates very infrequently and you do not have to know the size of the container ahead of time for efficiency like std::vector and reserver
.
You can use the splice
function in std::list
to implement a pool. Add a new member variable PtrList m_Pool
. When you want to add a new object and the pool is not empty, assign the value to the first element in the pool and then splice it into the list. To erase an element, splice it from the list to the pool.
But if you don't care about the order of the elements, then a deque
can be much faster. If you want to erase an element in the middle, copy the last element onto the element you want to delete, then erase the last element.
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