Why does llvm::SmallVector split its storage? - c++

The implementation of llvm::SmallVector<T,N> is split amongst many types:
llvm::SmallVectorBase holds 3 void*s for begin, end, and capacity.
llvm::SmallVectorTemplateCommon<T> holds the first element of the small storage, as an appropriately aligned and sized char array.
llvm::SmallVector<T,N> holds the next N-1 elements of the small storage, as an array of appropriately aligned and sized chararrays.
Why is the storage split between the two class templates, as opposed to having the most derived class (SmallVector<T,N>) simply store all N elements and pass in pointers to this storage down to the base class? That is, where currently the default constructor does:
SmallVector() : SmallVectorImpl<T>(N) { }
A hypothetical different implementation could do:
SmallVector() : SmallVectorImpl<T>(&Storage, T * sizeof(N)) { }
and SmallVectorTemplateCommon would not have the FirstEl member. What is the advantage of the implementation as it stands?

Splitting the storage avoids storing the inline capacity (or an "is small" bit) in the "size-erased" type SmallVectorImpl.
SmallVectorImpl<T> can be used to reference any SmallVector<T, N> and supports all vector operations on it. When the the underlying storage grows the pointer cannot be passed to free if it's using the inline capacity. Comparing the current storage's address to the first element of the inline capacity is convenient and saves a bit of memory in SmallVector.

Related

How can I pass and store an array of variable size containing pointers to objects?

For my project I need to store pointers to objects of type ComplicatedClass in an array. This array is stored in a class Storage along with other information I have omitted here.
Here's what I would like to do (which obviously doesn't work, but hopefully explains what I'm trying to achieve):
class ComplicatedClass
{
...
}
class Storage
{
public:
Storage(const size_t& numberOfObjects, const std::array<ComplicatedClass *, numberOfObjects>& objectArray)
: size(numberOfObjects),
objectArray(objectArray)
{}
...
public:
size_t size;
std::array<ComplicatedClass *, size> objectArray;
...
}
int main()
{
ComplicatedClass * object1 = new ComplicatedClass(...);
ComplicatedClass * object2 = new ComplicatedClass(...);
Storage myStorage(2, {object1, object2});
...
return 0;
}
What I am considering is:
Using std::vector instead of std::array. I would like to avoid this because there are parts of my program that are not allowed to allocate memory on the free-store. As far as I know, std::vector would have to do that. As a plus I would be able to ditch size.
Changing Storage to a class template. I would like to avoid this because then I have templates all over my code. This is not terrible but it would make classes that use Storage much less readable, because they would also have to have templated functions.
Are there any other options that I am missing?
How can I pass and store an array of variable size containing pointers to objects?
By creating the objects dynamically. Most convenient solution is to use std::vector.
size_t size;
std::array<ComplicatedClass *, size> objectArray;
This cannot work. Template arguments must be compile time constant. Non-static member variables are not compile time constant.
I would like to avoid this because there are parts of my program that are not allowed to allocate memory on the free-store. As far as I know, std::vector would have to do that.
std::vector would not necessarily require the use of free-store. Like all standard containers (besides std::array), std::vector accepts an allocator. If you implement a custom allocator that doesn't use free-store, then your requirement can be satisfied.
Alternatively, even if you do use the default allocator, you could write your program in such way that elements are inserted into the vector only in parts of your program that are allowed to allocate from the free-store.
I thought C++ had "free-store" instead of heap, does it not?
Those are just different words for the same thing. "Free store" is the term used in C++. It's often informally called "heap memory" since "heap" is a data structure that is sometimes used to implement it.
Beginning with C++11 std::vector has the data() method to access the underlying array the vector is using for storage.
And in most cases a std::vector can be used similar to an array allowing you to take advantage of the size adjusting container qualities of std::vector when you need them or using it as an array when you need that. See https://stackoverflow.com/a/261607/1466970
Finally, you are aware that you can use vectors in place of arrays,
right? Even when a function expects c-style arrays you can use
vectors:
vector<char> v(50); // Ensure there's enough space
strcpy(&v[0], "prefer vectors to c arrays");

Can I use an int (as opposed to a char) array as a memory arena where objects are created with placement new?

The question concerns a home-grown container template (a kind of std::array/vector hybrid) which holds an untyped array for storage. New elements are added by a push_back() member function which copy-constructs an element via placement new. This way the container does not require the contained type to have a default constructor, and we avoid default-constructing potentially never needed elements.
Typically, such storage would be a character type like std::byte.
We are using a bouquet of compilers. One of them predates the C++11 alignment facilities like alignasor aligned_storage. Absent that, they all all need different pragmas or attributes to guarantee alignment. In order to simplify the build and avoid manual alignment computation noise, we had the idea to use an array of 32 bit integers which have an alignment guarantee. Here is the core of the implementation:
template <class T> struct vec
{
uint32_t storage[NUM];
T *freeMem;
vec() : freeMem((T *)storage) {}
T *push_back(const T &t) { return new (freeMem++) T(t); }
};
Notably, we use a typed (differently typed than the storage array) pointer to pass the storage location to placement new; we think that's not an aliasing violation because we don't read or write through it before the object with type T is created.
Also notable is that the newly created objects may incompletely straddle two or more of the original int objects in the storage area, if sizeof(T) is not a multiple of sizeof(uint32_t).
I would think we have neither aliasing nor object lifetime issues. Is that so?

Implementation of swap function for deque with constant complexity

It is said that the std::deque swap function takes constant time,not linear.
http://www.cplusplus.com/reference/deque/deque/swap-free/. How is that function implemented then?
All resizable standard library containers (that is, all except std::array) have to store their contents in dynamically allocated memory. That is because they can grow arbitrarily large and there's no way to store arbitrarily many objects in the fixed space occupied by the container object itself. In other words, it must be possible that container.size() > sizeof(container).
This means that the container object only stores a pointer to its contents, not the contents itself. Swapping two containers therefore means simply swapping these pointers. In extremely simplified form:
template <class T>
class Container
{
T *_begin, *_end;
friend void swap(Container &a, Container &b)
{
std::swap(a._begin, b._begin);
std::swap(a._end, b._end);
}
};
Of course, in practice, this is complicated by the presence of allocators etc., but the principle is the same.
The implementation of deque is typically hidden by using pimpl idiom (each deque holds a pointer to implementation). The pointers are then swapped. It might (also) be that the deque at least holds a pointer to its buffer, which is then swapped (with related members like size).
This post (copy and swap idiom) is related to how the swap might be implemented.

STL vector's implementation

I am wondering how is STL std::vector implemented.
To be exact, does STL vector hold a table of objects in it or a table of pointers to objects?
In practical implementation: is it better to have std::vector<char> that's size is about 10^8, or have an array of char?
The first option has obvious pros: iterating as in every other container, known size, automatic memory management, hard to do something really wrong.
The second option may use nine times less space (pointer is 64 bits where char is 8 bits) , but at at a cost of all those comfortable methods listed above.
I looked into
/usr/include/c++/4.8.2/bits/stl_vector.h
and saw that push_back() is implemented as below, but even examining alloc_traits.h gives me no clue how is it really done.
Type char was used only to show that the pointer's size is significant compared to the held value size.
I am using C++11.
void
push_back(const value_type& __x)
{
if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
{
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
__x);
++this->_M_impl._M_finish;
}
else
#if __cplusplus >= 201103L
_M_emplace_back_aux(__x);
#else
_M_insert_aux(end(), __x);
#endif
}
A vector manages a single, contiguous array of objects, so it doesn't need a pointer to every element. It only needs:
A pointer to the start of the array
A pointer (or index) marking the end of the used elements (i.e. the size)
A pointer (or index) marking the end of the allocated storage (i.e. the capacity)
(It also needs to store an allocator; but typically, that's stateless, and a decent implementation will use the "empty base class optimisation" to make sure it takes up no space in that case).
If you manage your own dynamic array, you will need at least two of these; so the extra cost of using a vector is a single pointer.
If you don't need dynamic allocation, then an automatic array (or std::array, if you want something more STLy) will be more efficient: it won't involve any heap allocations, or any extra storage. However, that's only possible if the size is known at compile-time, and there is a danger that a large array might overflow the stack.
std::vector holds a continuous storage block, dynamically (re-)allocated.
Think of it as if it was:
struct vector { size_t size, capacity; void *data; };
std::vector<T> holds a sequence of objects of type T in a guaranteed contiguous buffer.
Regarding the question
In practical implementation: is it better to have std::vector that's size is about 10^8, or have an array of char?
the size is irrelevant in itself.
However, if you allocate a large array of char as a local automatic variable, then likely you will run out of stack space, with very Undefined Behavior. You can avoid that by dynamically allocating the array. And one reasonable way to do that is to use a std::string (or a std::vector, but most likely this is a string).

Storing each element of a container class as an object

Recently, I have seen some Matrix and 1D array classes implemented in C++ where each individual element is wrapped as a class (e.g. Element class). Normally, we like to have containers like Matrix to contain actual elements (e.g. int) consecutive in a memory. Using a custom class for individual elements can give you some flexibility but what are the possible drawbacks?
To make it short, see the pseudo-code:
// 1st approach: Elements stored in their type.
template <class T>
class Matrix
{
T *m_data;
//..
};
// 2nd approach: Elements wrapped into a class
template<class T>
class Matrix
{
std::set<Element<T> > m_data; // or using std::vector<Element<T> > m_data
//..
}; // Element is a class representing single element of type T
what could be the implications of this second approach, specially if we need to use Matrix for large amount of data? Also, if we need to use this type with GPU programming(transfering to device memory back and forth)?
One drawback is a memory cost for each Element, which could be a performance concern in large collections. It will cost you at least a byte, probably more for the padding. The "sizeof" operator should tell you the cost.
If the class has no virtual functions, they will probably* be placed in contiguous memory with something like new Element[20] or std::vector<Element> v(20). As noted above, std::set and most other STL containers are not necessarily contiguous.
*I say "probably" because depending on the size of the actual type, the compiler might insert some padding, which you can probably control with #pragmas as needed.