I have an object-based adjacency list graph that consists of nodes and edges stored in a vector.
class Graph
{
struct NodePrivate
{
QVector<int> m_FromEdges, m_ToEdges;
};
struct EdgePrivate
{
int m_iFrom, m_iFromIndex, m_iTo, m_iToIndex;
};
//...
private:
QVector<NodePrivate> m_Nodes;
QVector<EdgePrivate> m_Edges;
};
In order to ensure contiguity (and constant speed) of the graph elements when removing them I do removals by swapping the last element with the one to be removed.
Now when user of the graph accesses the elements he does so via Node and Edge classes that are really just a wrapper around an index to the graph (and int).
class Item
{
//...
private:
int m_Index = -1; //or QSharedPointer<int>, see below
const Graph *m_Graph = nullptr;
};
class Node : public Item {};
class Edge : public Item {};
By removing a node or an edge these indexes might become invalid. I would like these to be persistent and insofar have tried (successfuly) two strategies but I do not like either of them very much:
1) Track all objects of type Node and Edge by registering them and deregistering them in constructor(s) and destructor respectively. These are then used to update the internal index whenever the relevant index changes. Biggest drawback of this is quite a lot of unnecessary registered temporaries.
2) The other option is to use smart-pointer approach by having the index dynamic (std::shared_ptr<int>). The index is then updated through that which is arguably better than updating all objects but at the cost of dynamic memory.
Is there any other option to implement this or improve upon these two designs?
First of all, I must admit that I don't think this problem can be solved perfectly. If you really want to make a lot of small changes to your graphs regularly, then you should switch to storing everything in linked lists instead of arrays. Also, you can just give up and say explicitly, that all Node and Edge handles are invalidated, just like std::vector::iterator-s are invalidated when you add an element to std::vector.
General discussion
In your case, vertices and adjacency lists are stored in arrays. Also, you have Node and Edge helpers, which allow user to point to the real nodes and edges whenever they want. I'll call them handles (they are like C++ iterators without any iteration capabilities). I see two different ways for maintaining the handles after changes.
The first way is to store direct pointer (or index) to a physical object in each handle, as you do it now. In this case you have to change all handles to an object, whenever the object is moved. That is why you absolutely must register all the handles you give away somewhere. This is exactly the first solution you suggest, and it leads to "heavy" handles: creating, deleting and copying handles becomes costly, regardless of whether any objects are actually moved.
The second way is to store pointer to some intermediate thing inside a Handle. Then make sure that this thing is never changed during object's lifetime, even if objects move. Clearly, the thing you point to in a handle must be something different from real physical index of your node of edge, since they change. In this approach you have to pay for indirect access each time a handle is dereferenced, so handle access becomes slightly heavier.
The second solution you propose is following this second approach. The intermediate things (which are being pointed to by your handles) are dynamically allocated int-s wrapped in shared_ptr, one never-moving int per object. You have to suffer at least from separate dynamic allocation (+deallocation) per each object created, also from reference counters updates. The reference counters can be easily removed: store unique_ptr-s in NodePrivate and EdgePrivate objects, and raw pointers in Node and Edge objects.
New approach
The other solution following the second approach is to use IDs as intermediate things pointed to be handles. Whenever you create a node, assign it a new node ID, same for edges. Assign IDs sequentally, starting from zero. Now you can maintain bidirectional correspondence between physical indices and these IDs, and update it in O(1) time on a change.
struct NodePrivate
{
QVector<int> m_FromEdges, m_ToEdges;
int id; //getting ID by physical index
};
struct EdgePrivate
{
int m_iFrom, m_iFromIndex, m_iTo, m_iToIndex;
int id; //getting ID by physical index
};
private:
QVector<NodePrivate> m_Nodes;
QVector<EdgePrivate> m_Edges;
QVector<int> m_NodeById; //getting physical index by ID
QVector<int> m_EdgeById; //getting physical index by ID
Note that these new m_NodeById and m_EdgeById vectors grow when objects are created, but do not shrink when objects are deleted. So you'll have empty cells in these arrays, which will only be deallocated when you delete your graph. So you can use this solution only if you are sure that the total amount of nodes and edges created during graph's lifetime is relatively small, since you take 4 bytes of memory per each such object.
Improving memory consumption
You might have already noticed the similarity between the new solution just presented and the shared_ptr-based solution you had. In fact, if we do not distinguish C pointers and array indices, then they are the same, except for: in your solution int-s are allocated in heap, but in the proposed solution int-s are allocated in a pool allocator.
A very well-known improvement to a no-free pool allocator is the technique known as 'free lists', and we can apply it to the solution described above. Instead of always assigning new IDs to created objects, we allow to reuse them. In order to achieve that, we store a stack of free IDs, When an object is removed, we add its ID to this stack. When a new object is created, we take an ID for it from the stack. If stack is empty, then we assign a new ID.
struct EdgePrivate
{
int m_iFrom, m_iFromIndex, m_iTo, m_iToIndex;
int id; //getting ID by physical index
};
private:
QVector<EdgePrivate> m_Edges;
QVector<int> m_EdgeById; //getting physical index by ID
QVector<int> m_FreeEdgeIds; //freelist: stack of IDs to be reused
This improvement makes sure that memory consumption is proportional of the maximum number of objects you ever had alive simultaneously (not the total number objects created). But of course it increases memory overhead per object even further. It saves you from malloc/free cost, but you can have issues with memory fragmentation for large graphs after many operations.
Related
When storing objects in standard collections, what considerations should one think of when deciding between storing values vs pointers? Where the owner of these collections (ObjectOwner) is allocated on the heap. For small structs/objects I've been storing values, while for large objects I've been storing pointers. My reasoning for this was that when standard containers are resized, their contents are copied (small copy ok, big copy bad). Any other things to keep in mind here?
class ObjectOwner
{
public:
SmallObject& getSmallObject(int smallObjectId);
HugeObject* getHugeObject(int hugeObjectId);
private:
std::map<int, SmallObject> mSmallObjectMap;
std::map<int, HugeObject *> mHugeObjectMap;
};
Edit:
an example of the above for more context:
Create/Delete items stored in std::map relatively infrequently ( a few times per second)
Get from std::map frequently (once per 10 milliseconds)
small object: < 32 bytes
huge object: > 1024 bytes
I would store object by value unless I need it through pointer. Possible reasons:
I need to store object hierarchy in a container (to avoid slicing)
I need shared ownership
There are possibly other reasons, but reasoning by size is only valid for some containers (std::vector for example) and even there you can make object moving cost minimal (reserve enough room in advance for example). You example for object size with std::map does not make any sense as std::map does not relocate objects when growing.
Note: return type of a method should not reflect the way you store it in a container, but rather it should be based on method semantics, ie what you would do if object is not found.
Only your profiler knows the answer for your SPECIFIC case; trying to use pointers rather than objects is a reliable way of minimising the amount you copy when a copy must be done (be it a resize of a vector or a copy of the whole container); but sometimes you WANT that copy because it's a snapshot for a thread inside a mutex, and it's faster to copy the container than to hold the mutex and deal with the data.
Some objects might not be possible to keep in any way other than pointer because they're not copyable.
Any performance gain by using container of pointer could be offset by costs of having to write more copy code, or repeated calls to new().
There's not a one answer fits all, and before you worry about the performance here you should establish where the performance problems really are. (Just repeating the point - use a profiler!)
This is question about good practice
Consider situation which is typical e.g. in 3D engines, physics engines, Finite element method or classical molecular dynamics solvers: You have objects of various types ( e.g. vertexes, edges, faces, bounded solid volumes ) which are cross-linked to each other (e.g. vertex know which edge are connected to it and vice versa). For performance and convenience of usage of such engine is crucial to be able quickly browse the network of such connections.
The question is: Is it better to point to the linked object by index in array, or by pointer ? ... especially performance-wise
typedef index_t uint16_t;
class Vertex{
Vec3 pos;
#ifdef BY_POINTER
Edge* edges[nMaxEdgesPerVertex];
Face* faces[nMaxFacesPerVertex];
#else
index_t edges[nMaxEdgesPerVertex];
index_t faces[nMaxFacesPerVertex];
#endif
}
class Edge{
Vec3 direction;
double length;
#ifdef BY_POINTER
Vertex* verts[2];
Faces* faces[nMaxFacesPerEdge];
#else
index_t verts[2];
index_t faces[nMaxFacesPerEdge];
#endif
}
class Face{
Vec3 normal;
double isoVal; // Plane equation: normal.dot(test_point)==isoVal
#ifdef BY_POINTER
Vertex* verts[nMaxVertsPerFace];
Edge* edges[nMaxEdgesPerFace];
#else
index_t verts[nMaxVertsPerFace];
index_t edges[nMaxEdgesPerFace];
#endif
}
#ifndef BY_POINTER
// we can use other datastructure here, such as std:vector or even some HashMap
int nVerts,nEdges,nFaces;
Vertex verts[nMaxVerts];
Edge edges[nMaxEdges];
Vertex faces[nMaxFaces];
#endif
Advantages of index:
using index can be more memory efficient when we use uint8_t or uint16_t for index instead of 32-bit or 64-bit pointer
index can carry some additional information ( e.g. about orientation of edge ) encoded in some bits;
the ordering of objects in array can carry some information about structure ( e.g. vertexes of cube could be ordered as {0b000,0b001,0b010,0b011,0b100,0b101,0b110,0b111} ). This information is not visible in pointers
Advantages of pointers:
We don't need to care about the arrays (or other data-structures) to store the objects. The objects can be simply allocated dynamically on the heap by new Vertex().
May be faster (?) because it does not need to add the base address of the array (?). But this is probably negligible with respect to memory latency (?)
using index can be more memory efficient when we use uint8_t or
uint16_t for index instead of 32-bit or 64-bit pointer
True. Having a small representation reduce the total size of the structure, reducing cache miss when traversing it.
index can carry some additional information ( e.g. about orientation
of edge ) encoded in some bits;
True.
We don't need to care about the arrays (or other data-structures) to
store the objects. The objects can be simply allocated dynamically on
the heap by new Vertex().
This is exactly what you don't want to do, speaking of performances.
You want to be sure Vertex are all packed, to avoid unnecessary cache missing.
In this case the array would save you from that wrong temptation.
You also want to access them sequentially, at least as much as possible, again to minimize cache miss.
How much you data structure are packed, small and accessed sequentially, is what actually drive performances.
May be faster (?) because it does not need to add the base address of
the array (?). But this is probably negligible with respect to memory
latency (?)
Possibly negligible. Probably depends on specific hardware and|or compiler.
Another missing advantage about index: easier to manage when you reallocate.
Consider a structure that can grow, like the following:
struct VertexList
{
std::vector<Vertex> vertices;
Vertex *start; // you can still access using vector if you prefer; start = &vertices[0];
}
If you are referencing a given vertex using pointers, and a reallocation occurs, you will end up with an invalid pointer.
For performance, what matters is the speed with which you can read the "next" element in whatever traversal order(s) are commonly done in the hot path.
For example, if you have a series of edges which represent some path, you would want them to be stored contiguously in memory (not using new for each one), in the order in which they are connected.
For this case (edges forming a path), it's clear that you do not need pointers, and you also do not need indexes. The connections are implied by the storage locations, so you just need a pointer to the first and perhaps the last edges (i.e. you can store the whole path in a std::vector<Edge>).
A second example illustrating domain knowledge that we can exploit: imagine we have a game supporting up to 8 players and want to store "who has visited each of the edges in a path." Again we do not need pointers nor indexes to refer to the 8 players. Instead, we can simply store a uint8_t inside each Edge and use the bits as flags for each player. Yes, this is low-level bit banging, but it gives us compact storage and efficient lookup once we have an Edge*. But if we need to do the lookup in the other direction, from players to Edges, the most efficient will be to store e.g. a vector of uint32_t inside each player and do indexing into the Edge array.
But what if edges can be added and removed in the middle of a path? Well then we might want a linked list. In this case we should use an intrusive linked list, and allocate the Edges in a pool. Having done this, we can store pointers to the Edges in each player object, and they will never change or need to be updated. We use an intrusive linked list with the understanding that an Edge is only ever part of a single path, so extrinsic storage of the linked-list pointers would be wasteful (std::list needs to store pointers to each object; an intrusive list does not).
So, each case must be considered individually, with as much knowledge of the domain as we can discover in advance. Neither pointers and indexing should be the first approach.
I am wanting to create C++ program with a class inside of it that will contain a storage composed of one or multiple dynamic arrays for storing doubles and will state if the storage is segmented if there's multiple arrays. Where can I go from here?
class DynamicArray {
public:
void addElemement(double num){
}
void removeElement(double num){
}
void segmentation(int x){
}
void merge(){
}
void print(){
}
};
int main(){
return 0;
}
(Upgrading my comment to an answer, in case it fits the bill ;) )
http://cpp-tip-of-the-day.blogspot.se/2013/11/how-is-stddeque-implemented.html
Not completely sure what you are after, but if you want the data structure inside your class to use more than one contiguous chunk of memory, look at std::deque. (STL). I am not sure if you easily can check how many chunks the deque has actually allocated, though... It may be useful to have the property of deque that growing the deque beyond a max capacity does not require re-allocating all memory used. EDIT: Checking nr chunks may be doable by wrapping the standard allocator for deque
It really depends on how you want to structure your storage. But as you are speaking of storage segmentation, I assume that you do not want to use a standard container but create your own low level structure.
Liminary remark: you did not spoke of what your class should functionally be: a set (only one copy of each value entered, what the remove method let imagine), a stack (always add new element at the end), an ordered set, using natural double comparisons or insertion order... so I will not give code, because each usage would get its implementation particularity.
The common implementation for non consecutive storage is a recursive tree. It is composed of chunks containing pointer to other chunks for non terminal (or non leaf) nodes and values (or pointer to values) for leaves. Those chunks have generally a fixed size. For example, it is (highly simplified) how files are implemented in a file system. If you give more details on what you actually w
Say I have a member variable called
Result m_result
class Result{
QList<Group *> m_groups
}
and Group
class Group{
QList<Data> m_data
}
As the program continues, each groups' QList of Data keeps growing. All groups are allocated in the heap ( hence the pointer). In essence, each group gets allocated ONCE in the heap. Does a particular group get re-allocated each time its QList grows? Also, does m_testResult get recopied over and over again because of the growth of Data members?
Inside QList, there will be pointers to some other objects or arrays which actually hold the data. As the list grows, new objects for this backing data will be allocated and the existing ones deleted. The code which you've shown doesn't have to worry about it, but if you are implement your own collections then you would. The objects themselves don't ever grow once they are allocated.
The details for QList can be found here - it uses an array of pointers to <T>, so isn't a linked list. As it uses pointers to the elements it does not have to copy the elements when the array is resized, just copying the pointers ( or possibly not if it's implemented in a similar fashion to a VList - I didn't see anything in the document to indicate which strategy it uses ), so a particular group will not get re-allocated each time its QList grows.
I don't know about QList in particular, but, in general, I would expect the following:
Upon adding a new element, the QList allocates (new) more memory and links the last element to the new element.
Upon removing an (existing) element, the element's predecessor is linked to its successor, and the memory holding the element is delete-ed.
This is the general principle on how any linked list works, such as std::list or std::slist
Objects never grow, but memory can be claimed and released repeatedly.
Simple example:
template <class P> class MyT
{
struct Item
{
public:
Item() {}
P *pData;
Item *next;
};
Item *head;
public:
...adding etc..
P* operator [](int index)
{
See question below:
}
};
Can I somehow make sure that the 'Item's are allocated in such a way that I can calculate the offset as follows: (#Steve:) Maybe not so clear here; what I need is a quick & easy way to get to the item without iterating through 10000 next's.
Item *pi = head + (sizeof(Item) * (index - 1));
A (clearer?) explanation of what I mean
Depends what you mean by "etc", in "adding, etc".
If "etc" includes, "removing", then you have the obvious problem that if you remove something in the middle of your list, then to maintain the indexing you have to shift everything after it downwards, which means updating all the next pointers.
I think perhaps you have simplified your example too far. If you require contiguous storage, use a vector (either of P, or of Item if there's something useful in Item that you've removed). If you have contiguous storage, then there's no benefit in having a next pointer, since you could just calculate it in Item, by adding 1 to this (then checking a bound to make sure you haven't reached the end).
If you absolutely need the public next pointer field, because it's part of some interface you're implementing that you can't change, then you could update it in the copy constructor and operator= for Item, and the interface had better forbid clients from writing to it.
There's no way to tell the memory allocator to allocate contiguous storage for separate allocations, if that's what you're asking. How would that even work? What if when you come to allocate, the "next" address is already occupied? What if the allocator imposes some overhead, for its own control structures (as almost all general-purpose allocators do), so that allocating an Item requires more than sizeof(Item) bytes? You can get the behaviour you want for a while with a fixed-size allocator, but eventually it needs a new block, or you delete something, and the relationship no longer holds.
I guess what you need is a std::list or std::vector.
However what you are trying would work if you have allocated sequential memory for Items and head is pointing to the start along with the modification suggested by Yossarian.
You can pre-allocate while initializing if this limit is crossed, allocate more and copy your contents to that area, freeing the existing.
Note: All these things are wrapped for you in the std containers.
"Memory Boundaries" can be forced through special gcc keywords
http://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html#Variable-Attributes
look at "alignment"
If I understand your question correctly you need to override operator new for Item and have the allocator pre-allocate enough memory to store all the Items you will ever need (which isn't possible in the general case but may be possible in your specific scenario). Then whenever an Item is newed up the allocator will return the next slot in the pre-allocated block.
All this looks un-realistic and the simple solution would be to use std::vector.
Item* pi = (head + (index - 1));
does the job. Btw, are you sure you want to do this? struct Item looks like linked-list structure (contains next).